org-favtable new version 2.1 replaces org-reftable
authorMarc-Oliver Ihm <marc@ihm.name>
Fri, 25 Jan 2013 20:14:46 +0000 (21:14 +0100)
committerMarc-Oliver Ihm <marc@ihm.name>
Fri, 25 Jan 2013 20:14:46 +0000 (21:14 +0100)
code/elisp/org-favtable.el [new file with mode: 0755]

diff --git a/code/elisp/org-favtable.el b/code/elisp/org-favtable.el
new file mode 100755 (executable)
index 0000000..d0ca875
--- /dev/null
@@ -0,0 +1,1669 @@
+;;; org-favtable.el --- Table of favorite references and links\r
+\r
+;; Copyright (C) 2011-2013 Free Software Foundation, Inc.\r
+\r
+;; Author: Marc-Oliver Ihm <org-favtable@ferntreffer.de>\r
+;; Keywords: hypermedia, matching\r
+;; Requires: org\r
+;; Download: http://orgmode.org/worg/code/elisp/org-favtable.el\r
+;; Version: 2.1.0\r
+\r
+;;; License:\r
+\r
+;; This program is free software; you can redistribute it and/or modify\r
+;; it under the terms of the GNU General Public License as published by\r
+;; the Free Software Foundation; either version 3, or (at your option)\r
+;; any later version.\r
+;;\r
+;; This program is distributed in the hope that it will be useful,\r
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+;; GNU General Public License for more details.\r
+;;\r
+;; You should have received a copy of the GNU General Public License\r
+;; along with GNU Emacs; see the file COPYING.  If not, write to the\r
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,\r
+;; Boston, MA 02110-1301, USA.\r
+\r
+;;; Commentary:\r
+\r
+;; Purpose:\r
+;;\r
+;;  Mark and find your favorite items and org-locations easily: Create and\r
+;;  update a lookup table of your favorite references and links. Often used\r
+;;  entries automatically bubble to the top of the table; entering some\r
+;;  keywords narrows it to just the matching entries; that way the right\r
+;;  one can be picked easily.\r
+;;\r
+;;  References are essentially small numbers (e.g. "R237" or "-455-"), as\r
+;;  created by this package; links are normal org-mode links.\r
+;;\r
+;;\r
+;; Setup:\r
+;;\r
+;;  - Add these lines to your .emacs:\r
+;;\r
+;;    (require 'org-favtable)\r
+;;    ;; Good enough to start, but later you should probably \r
+;;    ;; change this id, as will be explained below\r
+;;    (setq org-favtable-id "00e26bef-1929-4110-b8b4-7eb9c9ab1fd4")\r
+;;    ;; Optionally assign a key. Pick your own favorite.\r
+;;    (global-set-key (kbd "C-+") 'org-favtable)\r
+;;\r
+;;  - Just invoke `org-favtable', which will explain how to complete your\r
+;;    setup by creating the necessary table of favorites.\r
+;;\r
+;;\r
+;; Further reading:\r
+;;\r
+;;  Invoke `org-favtable' and pick one of its help options. You may also\r
+;;  read the documentation of `org-favtable-id' for setup instructions, of\r
+;;  `org-favtable' for regular usage and of `org-favtable--commands' for a\r
+;;  list of available commands.\r
+;;\r
+\r
+;;; Change Log:\r
+\r
+;;   [2013-01-25 Fr] Version 2.1.0:\r
+;;    - Added full support for links\r
+;;    - New commands "missing" and "statistics"\r
+;;    - Renamed the package from "org-reftable" to "org-favtable"\r
+;;    - Additional columns are required (e.g. "link"). Error messages will\r
+;;      guide you\r
+;;\r
+;;   [2012-12-07 Fr] Version 2.0.0:\r
+;;    - The format of the table of favorites has changed ! You need to bring\r
+;;      your existing table into the new format by hand (which however is\r
+;;      easy and explained below)\r
+;;    - Reference table can be sorted after usage count or date of last access\r
+;;    - Ask user explicitly, which command to invoke\r
+;;    - Renamed the package from "org-refer-by-number" to "org-reftable"\r
+\r
+;;   [2012-09-22 Sa] Version 1.5.0:\r
+;;    - New command "sort" to sort a buffer or region by reference number\r
+;;    - New commands "highlight" and "unhighlight" to mark references\r
+\r
+;;   [2012-07-13 Fr] Version 1.4.0:\r
+;;    - New command "head" to find a headline with a reference number\r
+\r
+;;   [2012-04-28 Sa] Version 1.3.0:\r
+;;    - New commands occur and multi-occur\r
+;;    - All commands can now be invoked explicitly\r
+;;    - New documentation\r
+;;    - Many bugfixes\r
+\r
+;;   [2011-12-10 Sa] Version 1.2.0:\r
+;;    - Fixed a bug, which lead to a loss of newly created reference numbers\r
+;;    - Introduced single and double prefix arguments\r
+;;    - Started this Change Log\r
+\r
+;;; Code:\r
+\r
+(require 'org-table)\r
+(require 'cl)\r
+\r
+(defvar org-favtable--version "2.1.0")\r
+(defvar org-favtable--preferred-command nil)\r
+\r
+(defvar org-favtable--commands '(occur head ref link enter leave goto + help reorder fill sort update highlight unhighlight missing statistics)\r
+  "List of commands known to org-favtable:\r
\r
+\r
+  occur: If you supply a keyword (text): Apply emacs standard\r
+    occur operation on the table of favorites; ask for a\r
+    string (keyword) to select lines. Occur will only show you\r
+    lines which contain the given keyword, so you can easily find\r
+    the right one. You may supply a list of words seperated by\r
+    comma (\",\"), to select lines that contain any or all of the\r
+    given words.\r
+\r
+    If you supply a reference number: Apply emacs standard\r
+    multi-occur operation all org-mode buffers to search for a\r
+    specific reference.\r
+\r
+    You may also read the note at the end of this help on saving\r
+    the keystroke RET to accept this frequent default command.\r
+\r
+  head: If invoked outside the table of favorites, ask for a\r
+    reference number and search for a heading containing it. If\r
+    invoked within favtable dont ask; rather use the reference or\r
+    link from the current line.\r
+\r
+  ref: Create a new reference, copy any previously selected text.\r
+    If already within reftable, fill in ref-column.\r
+\r
+  link: Create a new line in reftable with a link to the current node. \r
+    Do not populate the ref column; this can later be populated by\r
+    calling the \"fill\" command from within the reftable.\r
+\r
+  leave: Leave the table of favorites. If the last command has\r
+    been \"ref\", the new reference is copied and ready to yank.\r
+\r
+  enter: Just enter the node with the table of favorites.\r
+\r
+  goto: Search for a specific reference within the table of\r
+    favorites.\r
+\r
+  help: Show this list of commands.\r
+\r
+  +: Show all commands including the less frequently used ones\r
+    given below. If \"+\" is followd by enough letters of such a\r
+    command (e.g. \"+fi\"), then this command is invoked\r
+    directly.\r
+\r
+  reorder: Temporarily reorder the table of favorites, e.g. by\r
+    count, reference or last access.\r
+\r
+  fill: If either ref or link is missing, fill it.\r
+\r
+  sort: Sort a set of lines (either the active region or the\r
+    whole buffer) by the references found in each line.\r
+\r
+  update: For the given reference, update the line in the\r
+    favtable.\r
+\r
+  highlight: Highlight references in region or buffer.\r
+\r
+  unhighlight: Remove highlights.\r
+\r
+  missing : Search for missing reference numbers (which do not\r
+    appear in the reference table). If requested, add additional\r
+    lines for them, so that the command \"new\" is able to reuse\r
+    them.\r
+\r
+  statistics : Show some statistics (e.g. minimum and maximum\r
+    reference) about favtable.\r
+\r
+\r
+When prompting for a command, org-favtable puts the most likely\r
+chosen one (e.g. \"occur\" or \"ref\") at the front of the list,\r
+so that you may just type RET. \r
+\r
+If this command needs additional input (like e.g. \"occur\"), you\r
+may supply this input right away, although you are still beeing\r
+prompted for the command.\r
+\r
+")\r
+\r
+(defvar org-favtable--commands-some '(occur head ref link leave enter goto + help))\r
+\r
+(defvar org-favtable--columns nil)\r
+\r
+(defvar org-favtable-id nil \r
+  "Id of the Org-mode node, which contains the favorite table.\r
+\r
+Read below, on how to set up things. See the help options\r
+\"usage\" and \"commands\" for normal usage after setup.\r
+\r
+Setup requires two steps:\r
+\r
+ - Adjust your .emacs initialization file\r
+\r
+ - Create a suitable org-mode node\r
+\r
+\r
+Here are the lines, you need to add to your .emacs:\r
+\r
+  (require 'org-favtable)\r
+  ;; Good enough to start, but later you should probably \r
+  ;; change this id, as will be explained below\r
+  (setq org-favtable-id \"00e26bef-1929-4110-b8b4-7eb9c9ab1fd4\")\r
+  ;; Optionally assign a key. Pick your own favorite.\r
+  (global-set-key (kbd \"C-+\") 'org-favtable)\r
+\r
+Do not forget to restart emacs to make these lines effective.\r
+\r
+\r
+As a second step you need to create the org-mode node, where your\r
+reference numbers and links will be stored. It may look like\r
+this:\r
+\r
+  * org-favtable\r
+    :PROPERTIES:\r
+    :ID:       00e26bef-1929-4110-b8b4-7eb9c9ab1fd4\r
+    :END:\r
+\r
+\r
+    |     |      | Comment, description, details  |         |         |               |\r
+    | ref | link | ;c                             | count;s | created | last-accessed |\r
+    |     | <4>  | <30>                           |         |         |               |\r
+    |-----+------+--------------------------------+---------+---------+---------------|\r
+    | R1  |      | My first reference             |         |         |               |\r
+\r
+\r
+You may just copy this node into one of your org-files.  Many\r
+things however can or should be adjusted:\r
+\r
+ - The node needs not be a top level node.\r
+\r
+ - Its name is completely at you choice. The node is found\r
+   through its ID.\r
+\r
+ - There are three lines of headings above the first hline. The\r
+   first one is ignored by org-favtable, and you can use them to\r
+   give meaningful names to columns; the second line contains\r
+   configuration information for org-favtable; please read\r
+   further below for its format. The third line is optional and\r
+   may contain width-informations (e.g. <30>) only.\r
+\r
+ - The sequence of columns does not matter. You may reorder them\r
+   any way you like; e.g. make the comment-column the last\r
+   columns within the table. Columns ar found by their name,\r
+   which appears in the second heading-line.\r
+\r
+ - You can add further columns or even remove the\r
+   \"Comment\"-column. All other columns from the\r
+   example (e.g. \"ref\", \"link\", \"count\", \"created\" and\r
+   \"last-accessed\") are required.\r
+\r
+ - Your references need not start at \"R1\"; However, having an\r
+   initial row is required (it serves as a template for subsequent\r
+   references).\r
+\r
+ - Your reference need not have the form \"R1\"; you may just as\r
+   well choose any text, that contains a single number,\r
+   e.g. \"reference-{1}\" or \"#7\" or \"++17++\" or \"-344-\". The\r
+   function `org-favtable' will inspect your first reference and\r
+   create all subsequent references in the same way.\r
+    \r
+ - You may want to change the ID-Property of the node above and\r
+   create a new one, which is unique (and not just a copy of\r
+   mine). You need to change it in the lines copied to your .emacs\r
+   too. However, this is not strictly required to make things\r
+   work, so you may do this later, after trying out this package.\r
+\r
+\r
+Optionally you may tweak the second header line to adjust\r
+`org-favtable' a bit. In the example above it looks like this\r
+ (with spaces collapsed):\r
+\r
+\r
+    | ref | link | ;c | count;s | created | last-accessed |\r
+\r
+\r
+The different fields have different meanings:\r
+\r
+ - ref : This denotes the column which contains you references\r
+\r
+ - link : Column for org-mode links, which can be used to access\r
+   locations within your files.\r
+\r
+ - ;c : The flag \"c\" (\"c\" for \"copy\") denotes this column\r
+   as the one beeing copied on command \"leave\". In the example\r
+   above, it is also the comment-column.\r
+\r
+ - count;s : this is the column which counts, how many time this\r
+   line has been accessed (which is the key-feature of this\r
+   package). The flag \"s\" stands for \"sort\", so the table is\r
+   sorted after this column. You may also sort after columns\r
+   \"ref\" or \"last-accessed\".\r
+\r
+ - created : Date when this line was created.\r
+\r
+ - last-accessed : Date and time, when this line was last accessed.\r
+\r
+\r
+After this two-step setup process you may invoke `org-favtable'\r
+to create a new favorite. Read the help option \"usage\" for\r
+instructions on normal usage, read the help option \"commands\"\r
+for help on single commands.\r
+\r
+")\r
+\r
+\r
+(defvar org-favtable--windowconfig-before nil)\r
+(defvar org-favtable--marker-outside-before nil)\r
+(defvar org-favtable--last-action nil)\r
+(defvar org-favtable--occur-buffer nil)\r
+(defvar org-favtable--ref-regex nil)\r
+(defvar org-favtable--ref-format nil)\r
+\r
+\r
+\r
+(defun org-favtable  (&optional what search search-is-link) \r
+  "Mark and find your favorite items and org-locations easily:\r
+Create and update a lookup table of your favorite references and\r
+links. Often used entries automatically bubble to the top of the\r
+table; entering some keywords narrows it to just the matching\r
+entries; that way the right one can be picked easily.\r
+\r
+References are essentially small numbers (e.g. \"R237\" or\r
+\"-455-\"), as created by this package; links are normal org-mode\r
+links. Within org-favtable, both are denoted as favorites.\r
+\r
+\r
+Read below for a detailed description of this function. See the\r
+help option \"setup\" or read the documentation of\r
+`org-favtable-id' for setup instructions.\r
+\r
+The function `org-favtable' operates on a dedicated table (called\r
+the table or favorites or favtable, for short) within a special\r
+Org-mode node. The node has to be created as part of your initial\r
+setup. Each line of the favorite table contains:\r
+\r
+ - A reference (optional)\r
+\r
+ - A link (optional)\r
+\r
+ - A number; counting, how often each reference has been\r
+   used. This number is updated automatically and the table can\r
+   be sorted according to it, so that most frequently used\r
+   references appear at the top of the table and can be spotted\r
+   easily.\r
+\r
+ - Its respective creation date\r
+\r
+ - Date and time of last access. This column can alternatively be\r
+   used to sort the table.\r
+\r
+To be useful, your table of favorites should probably contain a\r
+column with comments too, which allows lines to be selected by\r
+keywords.\r
+\r
+The table of favorites is found through the id of the containing\r
+node; this id should be stored within `org-favtable-id' (see there\r
+for details).\r
+\r
+\r
+The function `org-favtable' is the only interactive function of\r
+this package and its sole entry point; it offers several commands\r
+to create, find and look up these favorites (references and\r
+links). All of them are explained within org-favtable's help.\r
+\r
+\r
+Finally, org-favtable can also be invoked from elisp; the two\r
+optional arguments accepted are:\r
+\r
+         search : string to search for\r
+           what : symbol of the command to invoke\r
+ search-is-link : t, if argument search is actually a link\r
+\r
+An example would be:\r
+\r
+ (org-favtable \"237\" 'head)   ;; find heading with ref 237\r
+\r
+"\r
+\r
+  (interactive "P")\r
+\r
+(let (within-node        ; True, if we are within node with favtable\r
+      result-is-visible  ; True, if node or occur is visible in any window\r
+      ref-node-buffer-and-point ; cons with buffer and point of favorites node\r
+      below-cursor              ; word below cursor\r
+      active-region             ; active region (if any)\r
+      link-id                   ; link of starting node, if required\r
+      guarded-search            ; with guard against additional digits\r
+      search-is-ref             ; true, if search is a reference\r
+      commands                ; currently active set of selectable commands\r
+      what-adjusted           ; True, if we had to adjust what\r
+      what-input    ; Input on what question (need not necessary be "what")\r
+      reorder-once  ; Column to use for single time sorting\r
+      parts         ; Parts of a typical reference number (which\r
+                    ; need not be a plain number); these are:\r
+      head               ; Any header before number (e.g. "R")\r
+      maxref             ; Maximum number from reference table (e.g. "153")\r
+      tail               ; Tail after number (e.g. "}" or "")\r
+      ref-regex          ; Regular expression to match a reference\r
+      has-reuse          ; True, if table contains a line for reuse\r
+      numcols            ; Number of columns in favtable\r
+      kill-new-text      ; Text that will be appended to kill ring\r
+      message-text       ; Text that will be issued as an explanation,\r
+                                                  ; what we have done\r
+      initial-ref-or-link                    ; initial position in reftable\r
+      )\r
+\r
+  ;;\r
+  ;; Examine current buffer and location, before turning to favtable\r
+  ;;\r
+\r
+  ;; Get the content of the active region or the word under cursor\r
+  (if (and transient-mark-mode\r
+           mark-active)\r
+      (setq active-region (buffer-substring (region-beginning) (region-end))))\r
+  (setq below-cursor (thing-at-point 'symbol))\r
+\r
+\r
+  ;; Find out, if we are within favable or not\r
+  (setq within-node (string= (org-id-get) org-favtable-id))\r
+\r
+  ;; Find out, if point in any window is within node with favtable\r
+  (mapc (lambda (x) (with-current-buffer (window-buffer x)\r
+                      (when (or \r
+                             (string= (org-id-get) org-favtable-id)\r
+                             (eq (window-buffer x) \r
+                                 org-favtable--occur-buffer))\r
+                        (setq result-is-visible t))))\r
+        (window-list))\r
+        \r
+\r
+\r
+  ;;\r
+  ;; Get decoration of references and highest reference from favtable\r
+  ;;\r
+\r
+\r
+  ;; Save initial ref or link\r
+  (if (and within-node\r
+           (org-at-table-p))\r
+      (setq initial-ref-or-link \r
+            (or (org-favtable--get-field 'ref)\r
+                (org-favtable--get-field 'link))))\r
+\r
+  ;; Find node\r
+  (setq ref-node-buffer-and-point (org-favtable--id-find))\r
+  (unless ref-node-buffer-and-point\r
+    (org-favtable--report-setup-error \r
+     (format "Cannot find node with id \"%s\"" org-favtable-id)))\r
+\r
+  ;; Get configuration of reftable; catch errors\r
+  (let ((error-message\r
+         (catch 'content-error\r
+\r
+           (with-current-buffer (car ref-node-buffer-and-point)\r
+             (unless (string= (org-id-get) org-favtable-id)\r
+\r
+               ;; Get marker for point within reftable-buffer, but only if outside\r
+               ;; of reftable (if point is within reftable, we will try to stay at\r
+               ;; the same ref)\r
+               (setq org-favtable--marker-outside-before (point-marker))\r
+               (goto-char (cdr ref-node-buffer-and-point)))\r
+\r
+             ;; parse table while still within buffer\r
+             (save-excursion\r
+               (setq parts (org-favtable--parse-and-adjust-table)))\r
+              \r
+             nil))))\r
+    (when error-message \r
+      (org-pop-to-buffer-same-window (car ref-node-buffer-and-point))\r
+      (org-reveal)\r
+      (error error-message)))\r
+\r
+  ;; Give names to parts of configuration\r
+  (setq head (nth 0 parts))\r
+  (setq maxref (nth 1 parts))\r
+  (setq tail (nth 2 parts))\r
+  (setq numcols (nth 3 parts))\r
+  (setq ref-regex (nth 4 parts))\r
+  (setq has-reuse (nth 5 parts))\r
+  (setq org-favtable--ref-regex ref-regex)\r
+  (setq org-favtable--format (concat head "%d" tail))\r
+\r
+  ;;\r
+  ;; Find out, what we are supposed to do\r
+  ;;\r
+\r
+  (if (equal what '(4)) (setq what 'leave))\r
+\r
+  ;; Set preferred action, that will be the default choice\r
+  (setq org-favtable--preferred-command\r
+        (if within-node\r
+            (if (memq org-favtable--last-action '(ref link))\r
+                'leave\r
+              'occur)\r
+          (if active-region\r
+              'ref\r
+            (if (and below-cursor (string-match ref-regex below-cursor))\r
+                'occur\r
+              nil))))\r
+    \r
+  ;; Ask user, what to do\r
+  (unless what\r
+    (setq commands (copy-list org-favtable--commands-some))\r
+    (while (progn\r
+             (setq what-input\r
+                   (org-icompleting-read \r
+                    "Please choose: " \r
+                    (mapcar 'symbol-name \r
+                            ;; Construct unique list of commands with\r
+                            ;; preferred one at front\r
+                            (delq nil (delete-dups \r
+                                       (append \r
+                                        (list org-favtable--preferred-command)\r
+                                        commands))))\r
+                    nil nil))\r
+\r
+\r
+             ;; if input starts with "+" any command (not only some) may follow\r
+             (when (string= (substring what-input 0 1) "+")\r
+               ;; make all commands available for selection\r
+               (setq commands (copy-list org-favtable--commands))\r
+               (unless (string= what-input "+") \r
+                 ;; not just "+", use following string\r
+                 (setq what-input (substring what-input 1))\r
+                 \r
+                 (let ((completions\r
+                        ;; get list of possible completions for what-input\r
+                        (all-completions what-input (mapcar 'symbol-name org-favtable--commands))))\r
+                   ;; use it, if unambigously\r
+                   (if (= (length completions) 1)\r
+                       (setq what-input (car completions))))))\r
+\r
+\r
+             (setq what (intern what-input))\r
+               \r
+             ;; user is not required to input one of the commands; if\r
+             ;; not, take the first one and use the original input for\r
+             ;; next question\r
+             (if (memq what commands)\r
+                 ;; input matched one element of list, dont need original\r
+                 ;; input any more\r
+                 (setq what-input nil)\r
+               ;; what-input will be used for next question, use first\r
+               ;; command for what\r
+               (setq what (or org-favtable--preferred-command\r
+                              (first commands)))\r
+               ;; remove any trailing dot, that user might have added to\r
+               ;; disambiguate his input\r
+               (if (equal (substring what-input -1) ".")\r
+                   ;; but do this only, if dot was really necessary to\r
+                   ;; disambiguate\r
+                   (let ((shortened-what-input (substring what-input 0 -1)))\r
+                     (unless (test-completion shortened-what-input \r
+                                              (mapcar 'symbol-name \r
+                                                      commands))\r
+                       (setq what-input shortened-what-input)))))\r
+                     \r
+             ;; ask for reorder in loop, because we have to ask for\r
+             ;; what right again\r
+             (if (eq what 'reorder)\r
+                 (setq reorder-once\r
+                       (intern\r
+                        (org-icompleting-read \r
+                         "Please choose column to reorder reftable once: " \r
+                         (mapcar 'symbol-name '(ref count last-accessed))\r
+                         nil t))))\r
+               \r
+             ;; maybe ask initial question again\r
+             (memq what '(reorder +)))))\r
+\r
+\r
+  ;;\r
+  ;; Get search, if required\r
+  ;;\r
+\r
+  ;; These actions need a search string:\r
+  (when (memq what '(goto occur head update))\r
+\r
+    ;; Maybe we've got a search string from the arguments\r
+    (unless search\r
+      (let (search-from-table\r
+            search-from-cursor)\r
+          \r
+        ;; Search string can come from several sources:\r
+        ;; From ref column of table\r
+        (when within-node\r
+          (setq search-from-table (org-favtable--get-field 'ref)))      \r
+        ;; From string below cursor\r
+        (when (and (not within-node)\r
+                   below-cursor\r
+                   (string-match (concat "\\(" ref-regex "\\)") \r
+                                 below-cursor))\r
+          (setq search-from-cursor (match-string 1 below-cursor)))\r
+          \r
+        ;; Depending on requested action, get search from one of the sources above\r
+        (cond ((eq what 'goto)\r
+               (setq search (or what-input search-from-cursor)))\r
+              ((memq what '(head occur))\r
+               (setq search (or what-input search-from-table search-from-cursor))))))\r
+\r
+\r
+    ;; If we still do not have a search string, ask user explicitly\r
+    (unless search\r
+        \r
+      (if what-input \r
+          (setq search what-input)\r
+        (setq search (read-from-minibuffer\r
+                      (cond ((memq what '(occur head))\r
+                             "Text or reference number to search for: ")\r
+                            ((eq what 'goto)\r
+                             "Reference number to search for, or enter \".\" for id of current node: ")\r
+                            ((eq what 'update)\r
+                             "Reference number to update: ")))))\r
+\r
+      (if (string-match "^\\s *[0-9]+\\s *$" search)\r
+          (setq search (format "%s%s%s" head (org-trim search) tail))))\r
+      \r
+    ;; Clean up and examine search string\r
+    (if search (setq search (org-trim search)))\r
+    (if (string= search "") (setq search nil))\r
+    (setq search-is-ref (string-match ref-regex search))\r
+\r
+    ;; Check for special case\r
+    (when (and (memq what '(head goto))\r
+               (string= search "."))\r
+      (setq search (org-id-get))\r
+      (setq search-is-link t))\r
+\r
+    (when search-is-ref\r
+      (setq guarded-search (org-favtable--make-guarded-search search)))\r
+\r
+    ;;\r
+    ;; Do some sanity checking before really starting\r
+    ;;\r
+\r
+    ;; Correct requested action, if nothing to search\r
+    (when (and (not search)\r
+               (memq what '(search occur head)))\r
+      (setq what 'enter)\r
+      (setq what-adjusted t))\r
+\r
+    ;; For a proper reference as input, we do multi-occur\r
+    (if (and (string-match ref-regex search)\r
+             (eq what 'occur))\r
+        (setq what 'multi-occur))\r
+\r
+    ;; Check for invalid combinations of arguments; try to be helpful\r
+    (when (and (memq what '(head goto))\r
+               (not search-is-link)\r
+               (not search-is-ref))\r
+      (error "Can do '%s' only for a reference or link (not '%s'), try 'occur' to search for text" what search)))\r
+\r
+    \r
+  ;;\r
+  ;; Prepare\r
+  ;;\r
+\r
+  ;; Get link if required before moving in\r
+  (if (eq what 'link)\r
+      (setq link-id (org-id-get-create)))\r
+\r
+  ;; Move into table, if outside\r
+  (when (memq what '(enter ref link goto occur multi-occur missing statistics))\r
+    ;; Save current window configuration\r
+    (when (or (not result-is-visible)\r
+              (not org-favtable--windowconfig-before))\r
+      (setq org-favtable--windowconfig-before (current-window-configuration)))\r
+\r
+    ;; Switch to favtable\r
+    (org-pop-to-buffer-same-window (car ref-node-buffer-and-point))\r
+    (goto-char (cdr ref-node-buffer-and-point))\r
+    (show-subtree)\r
+    (org-show-context)\r
+\r
+    ;; sort favtable\r
+    (org-favtable--sort-table reorder-once))\r
+\r
+  ;; Goto back to initial ref, because reformatting of table above might\r
+  ;; have moved point\r
+  (when initial-ref-or-link\r
+    (while (and (org-at-table-p)\r
+                (not (or\r
+                      (string= initial-ref-or-link (org-favtable--get-field 'ref))\r
+                      (string= initial-ref-or-link (org-favtable--get-field 'link)))))\r
+      (forward-line))\r
+    ;; did not find ref, go back to top\r
+    (if (not (org-at-table-p)) (goto-char top)))\r
+\r
+\r
+  ;;\r
+  ;; Actually do, what is requested\r
+  ;;\r
+\r
+  (cond\r
+\r
+\r
+   ((eq what 'help)\r
+      \r
+    (let ((help-what\r
+           ;; which sort of help ?\r
+           (intern\r
+            (concat \r
+             "help-"\r
+             (org-icompleting-read \r
+              "Help on: "\r
+              (mapcar 'symbol-name '(commands usage setup version example)) \r
+              nil t)))))\r
+\r
+      ;; help is taken from docstring of functions or variables\r
+      (cond ((eq help-what 'help-commands)\r
+             (org-favtable--show-help 'org-favtable--commands))\r
+            ((eq help-what 'help-usage)\r
+             (org-favtable--show-help 'org-favtable))\r
+            ((eq help-what 'help-setup)\r
+             (org-favtable--show-help 'org-favtable-id))\r
+            ((eq help-what 'help-version)\r
+             (org-favtable-version)))))\r
+\r
+\r
+   ((eq what 'multi-occur) \r
+      \r
+    ;; Conveniently position cursor on number to search for\r
+    (org-favtable--goto-top)\r
+    (let (found (initial (point)))\r
+      (while (and (not found)\r
+                  (forward-line)\r
+                  (org-at-table-p))\r
+        (save-excursion \r
+          (setq found (string= search \r
+                               (org-favtable--get-field 'ref)))))\r
+      (if found \r
+          (org-favtable--update-line nil)\r
+        (goto-char initial)))\r
+\r
+    ;; Construct list of all org-buffers\r
+    (let (buff org-buffers)\r
+      (dolist (buff (buffer-list))\r
+        (set-buffer buff)\r
+        (if (string= major-mode "org-mode")\r
+            (setq org-buffers (cons buff org-buffers))))\r
+\r
+      ;; Do multi-occur\r
+      (multi-occur org-buffers guarded-search)\r
+      (if (get-buffer "*Occur*")\r
+          (progn \r
+            (setq message-text (format "multi-occur for '%s'" search))\r
+            (setq org-favtable--occur-buffer (get-buffer "*Occur*"))\r
+            (other-window 1)\r
+            (toggle-truncate-lines 1))\r
+        (setq message-text (format "Did not find '%s'" search)))))\r
+\r
+\r
+   ((eq what 'head)\r
+\r
+    (let (link)\r
+      ;; link either from table or passed in as argument\r
+        \r
+      ;; try to get link\r
+      (if search-is-link \r
+          (setq link (org-trim search))\r
+        (if (and within-node\r
+                 (org-at-table-p))\r
+            (setq link (org-favtable--get-field 'link))))\r
+\r
+      ;; use link if available\r
+      (if (and link\r
+               (not (string= link "")))\r
+          (progn \r
+            (org-id-goto link)\r
+            (org-favtable--update-line search)\r
+            (setq message-text "Followed link"))\r
+\r
+        (message (format "Scanning headlines for '%s' ..." search))\r
+        (let (buffer point)\r
+          (if (catch 'found\r
+                (progn\r
+                  ;; loop over all headlines, stop on first match\r
+                  (org-map-entries \r
+                   (lambda () \r
+                     (when (looking-at (concat ".*" guarded-search))\r
+                       ;; remember location and bail out\r
+                       (setq buffer (current-buffer))\r
+                       (setq point (point))\r
+                       (throw 'found t)))          \r
+                   nil 'agenda)\r
+                  nil))\r
+\r
+              (progn\r
+                (org-favtable--update-line search)\r
+                (setq message-text (format "Found '%s'" search))\r
+                (org-pop-to-buffer-same-window buffer)\r
+                (goto-char point)\r
+                (org-reveal))\r
+            (setq message-text (format "Did not find '%s'" search)))))))\r
+\r
+\r
+   ((eq what 'leave)\r
+\r
+    (when result-is-visible\r
+\r
+      ;; If we are within the occur-buffer, switch over to get current line\r
+      (if (and (string= (buffer-name) "*Occur*")\r
+               (eq org-favtable--last-action 'occur))\r
+          (occur-mode-goto-occurrence))\r
+\r
+      (let (copy-column)              \r
+        ;; Try to copy requested column\r
+        (setq copy-column (org-favtable--column-num (if (eq org-favtable--last-action 'ref)\r
+                                                        'goto\r
+                                                      'copy)))\r
+            \r
+        ;; Add to kill ring\r
+        (if (memq org-favtable--last-action '(ref enter goto occur))\r
+            (setq kill-new-text \r
+                  (org-trim (org-table-get-field copy-column))))))\r
+\r
+    ;; Restore position within buffer with favtable\r
+    (with-current-buffer (car ref-node-buffer-and-point)\r
+      (when org-favtable--marker-outside-before\r
+        (goto-char (marker-position org-favtable--marker-outside-before))\r
+        (move-marker org-favtable--marker-outside-before nil)))\r
+\r
+    ;; Restore windowconfig\r
+    (if org-favtable--windowconfig-before \r
+        (progn  \r
+          ;; Restore initial window configuration\r
+          (set-window-configuration org-favtable--windowconfig-before)\r
+          (setq org-favtable--windowconfig-before nil)\r
+          ;; Goto initial position\r
+          (recenter)\r
+          (setq message-text "Back"))\r
+      ;; We did not have a window-configuration to restore, so we cannot\r
+      ;; pretend we have returned back\r
+      (setq message-text "Cannot leave; nowhere to go to")\r
+      (setq kill-new-text nil)))\r
+\r
+\r
+   ((eq what 'goto)\r
+\r
+    ;; Go downward in table to requested reference\r
+    (let (found (initial (point)))\r
+      (org-favtable--goto-top)\r
+      (while (and (not found)\r
+                  (forward-line)\r
+                  (org-at-table-p))\r
+        (save-excursion \r
+          (setq found \r
+                (string= search \r
+                         (org-favtable--get-field \r
+                          (if search-is-link 'link 'ref))))))\r
+      (if found\r
+          (progn\r
+            (setq message-text (format "Found '%s'" search))\r
+            (org-favtable--update-line nil)\r
+            (org-table-goto-column (org-favtable--column-num 'ref))\r
+            (if (looking-back " ") (backward-char)))\r
+        (setq message-text (format "Did not find '%s'" search))\r
+        (goto-char initial)\r
+        (forward-line)\r
+        (setq what 'missed))))\r
+\r
+\r
+   ((eq what 'occur)\r
+\r
+    ;; search for string: occur\r
+    (let (search-regexp\r
+          all-or-any\r
+          (search-words (split-string search "," t)))\r
+            \r
+      (if (< (length search-words) 2)\r
+          ;; only one word to search; use it as is\r
+          (setq search-regexp search)\r
+        ;; construct regexp to match any of the words (maybe throw out some matches later)\r
+        (setq search-regexp \r
+              (mapconcat (lambda (x) (concat "\\(" x "\\)")) search-words "\\|"))\r
+        (setq all-or-any\r
+              (intern \r
+               (org-icompleting-read \r
+                "Two or more words have been specified; show lines, that match: " '("all" "any")))))\r
+            \r
+      (save-restriction\r
+        (org-narrow-to-subtree)\r
+        (occur search-regexp)\r
+        (widen)\r
+        (if (get-buffer "*Occur*")\r
+            (with-current-buffer "*Occur*"\r
+\r
+              ;; install helpful keyboard-shortcuts within occur-buffer\r
+              (let ((keymap (make-sparse-keymap)))\r
+                (set-keymap-parent keymap occur-mode-map)\r
+\r
+                (define-key keymap (kbd "RET") \r
+                  (lambda () (interactive) \r
+                    (org-favtable--occur-helper 'head)))\r
+\r
+                (define-key keymap (kbd "<C-return>") \r
+                  (lambda () (interactive) \r
+                    (org-favtable--occur-helper 'multi-occur)))\r
+\r
+                (define-key keymap (kbd "<M-return>") \r
+                  (lambda () (interactive) \r
+                    (org-favtable--occur-helper 'goto)))\r
+\r
+                (define-key keymap (kbd "<C-M-return>") \r
+                  (lambda () (interactive) \r
+                    (org-favtable--occur-helper 'update)))\r
+\r
+                (use-local-map keymap))\r
+\r
+              ;; Brush up occur buffer\r
+              (other-window 1)\r
+              (toggle-truncate-lines 1)\r
+              (let ((inhibit-read-only t)) \r
+                ;; insert some help text\r
+                (insert (substitute-command-keys \r
+                         "Type RET to find heading, C-RET for multi-occur, M-RET to go to occurence and C-M-RET to update line in reftable.\n\n"))\r
+                (forward-line 1)\r
+\r
+                ;; when matching all of multiple words, remove all lines that do not match one of the words\r
+                (when (eq all-or-any 'all)\r
+                  (mapc (lambda (x) (keep-lines x)) search-words))\r
+\r
+                ;; replace description from occur\r
+                (when all-or-any \r
+                  (forward-line -1)\r
+                  (kill-line)\r
+                  (let ((count (- (count-lines (point) (point-max)) 1)))\r
+                    (insert (format "%d %s for %s of %s" \r
+                                    count \r
+                                    (if (= count 1) "match" "matches")\r
+                                    all-or-any\r
+                                    search)))\r
+                  (forward-line)\r
+                  (beginning-of-line))\r
+                  \r
+                ;; Record link or reference for each line in\r
+                ;; occur-buffer, that is linked into reftable. Because if\r
+                ;; we later realign the reftable and then reuse the occur\r
+                ;; buffer, the original links might point nowehere.\r
+                (save-excursion\r
+                  (while (not (eq (point) (point-max)))\r
+                    (let ((beg (line-beginning-position))\r
+                          (end (line-end-position))\r
+                          pos ref link)\r
+\r
+                      ;; occur has saved the position into a special property\r
+                      (setq pos (get-text-property (point) 'occur-target))\r
+                      (when pos \r
+                        ;; but this property might soon point nowhere; so retrieve ref-or-link instead\r
+                        (with-current-buffer (marker-buffer pos)\r
+                          (goto-char pos)\r
+                          (setq ref (org-favtable--get-field 'ref))\r
+                          (setq link (org-favtable--get-field 'link))))\r
+                      ;; save as text property\r
+                      (put-text-property beg end 'org-favtable--ref ref)\r
+                      (put-text-property beg end 'org-favtable--link link))\r
+                    (forward-line))))\r
+                  \r
+              (setq message-text\r
+                    (format  "Occur for '%s'" search)))\r
+          (setq message-text\r
+                (format "Did not find any matches for '%s'" search))))))\r
+\r
+\r
+   ((memq what '(ref link))\r
+\r
+    ;; add a new row (or reuse existing one)\r
+    (let (new)\r
+\r
+      ;; go through table to find first entry to be reused\r
+      (when has-reuse\r
+        (org-favtable--goto-top)\r
+        ;; go through table\r
+        (while (and (org-at-table-p)\r
+                    (not new))\r
+          (when (string= \r
+                 (org-favtable--get-field 'count)\r
+                 ":reuse:")\r
+            (setq new (org-favtable--get-field 'ref))\r
+            (if new (org-table-kill-row)))\r
+          (forward-line)))\r
+        \r
+      ;; no ref to reuse; construct new reference\r
+      (unless new \r
+        (setq new (format "%s%d%s" head (1+ maxref) tail)))\r
+\r
+      ;; insert ref as very first row\r
+      (org-favtable--goto-top)\r
+      (org-table-insert-row)\r
+        \r
+      ;; fill special columns with standard values\r
+      (when (eq what 'ref)\r
+        (org-table-goto-column (org-favtable--column-num 'ref))\r
+        (insert new))\r
+      (when (eq what 'link)\r
+        (org-table-goto-column (org-favtable--column-num 'link))\r
+        (insert link-id))\r
+      (org-table-goto-column (org-favtable--column-num 'created))\r
+      (org-insert-time-stamp nil nil t)\r
+\r
+      ;; goto first nonempty field\r
+      (catch 'empty\r
+        (dotimes (col numcols)\r
+          (org-table-goto-column (+ col 1))\r
+          (if (string= (org-trim (org-table-get-field)) "")\r
+              (throw 'empty t)))\r
+        ;; none found, goto first\r
+        (org-table-goto-column 1))\r
+\r
+      (org-table-align)\r
+      (if active-region (setq kill-new-text active-region))\r
+      (if (eq what 'ref)\r
+          (setq message-text (format "Adding a new row with ref '%s'" new))\r
+        (setq message-text (format "Adding a new row linked to '%s'" link-id)))))\r
+\r
+\r
+   ((eq what 'enter)\r
+\r
+    ;; simply go into table\r
+    (org-favtable--goto-top)\r
+    (show-subtree)\r
+    (recenter)\r
+    (if what-adjusted\r
+        (setq message-text "Nothing to search for; at favtable")\r
+      (setq message-text "At favtable")))\r
+\r
+     \r
+   ((eq what 'fill)\r
+\r
+    ;; check if within reftable\r
+    (unless (and within-node\r
+                 (org-at-table-p))\r
+      (error "Not within table of favorites"))\r
+\r
+    ;; applies to missing refs and missing links alike\r
+    (let ((ref (org-favtable--get-field 'ref))\r
+          (link (org-favtable--get-field 'link)))\r
+\r
+      (if (and (not ref)\r
+               (not link))\r
+          ;; have already checked this during parse, check here anyway\r
+          (error "Columns ref and link are both empty in this line"))\r
+\r
+      ;; fill in new ref\r
+      (if (not ref)\r
+          (progn \r
+            (setq kill-new-text (format "%s%d%s" head (1+ maxref) tail))\r
+            (org-favtable--get-field 'ref kill-new-text)\r
+            (org-id-goto link)\r
+            (setq message-text "Filled reftable field with new reference"))\r
+\r
+        ;; fill in new link\r
+        (if (not link)\r
+            (progn\r
+              (setq guarded-search (org-favtable--make-guarded-search ref))\r
+              (message (format "Scanning headlines for '%s' ..." ref))\r
+              (let (link)\r
+                (if (catch 'found\r
+                      (org-map-entries \r
+                       (lambda () \r
+                         (when (looking-at (concat ".*" guarded-search))\r
+                           (setq link (org-id-get-create))\r
+                           (throw 'found t)))   \r
+                       nil 'agenda)\r
+                      nil)\r
+\r
+                    (progn\r
+                      (org-favtable--get-field 'link link)\r
+                      (setq message-text "Inserted link"))\r
+\r
+                  (setq message-text (format "Did not find reference '%s'" ref)))))\r
+          \r
+          ;; nothing is missing\r
+          (setq message-text "Columns 'ref' and 'link' are already filled; nothing to do")))))\r
+     \r
+\r
+   ((eq what 'sort)\r
+\r
+    ;; sort lines according to contained reference\r
+    (let (begin end where)\r
+      (catch 'aborted\r
+        ;; either active region or whole buffer\r
+        (if (and transient-mark-mode\r
+                 mark-active)\r
+            ;; sort only region\r
+            (progn\r
+              (setq begin (region-beginning))\r
+              (setq end (region-end))\r
+              (setq where "region"))\r
+          ;; sort whole buffer\r
+          (setq begin (point-min))\r
+          (setq end (point-max))\r
+          (setq where "whole buffer")\r
+          ;; make sure\r
+          (unless (y-or-n-p "Sort whole buffer ")\r
+            (setq message-text "Sort aborted")\r
+            (throw 'aborted nil)))\r
+          \r
+        (save-excursion\r
+          (save-restriction\r
+            (goto-char (point-min))\r
+            (narrow-to-region begin end)\r
+            (sort-subr nil 'forward-line 'end-of-line \r
+                       (lambda ()\r
+                         (if (looking-at (concat ".*" \r
+                                                 (org-favtable--make-guarded-search ref-regex 'dont-quote)))\r
+                             (string-to-number (match-string 1))\r
+                           0))))\r
+          (highlight-regexp ref-regex)\r
+          (setq message-text (format "Sorted %s from character %d to %d, %d lines" \r
+                                     where begin end\r
+                                     (count-lines begin end)))))))\r
+    \r
+\r
+   ((eq what 'update)\r
+\r
+    ;; simply update line in reftable\r
+    (save-excursion\r
+      (let ((ref-or-link (if search-is-link "link" "reference")))\r
+        (beginning-of-line)\r
+        (if (org-favtable--update-line search)\r
+            (setq message-text (format "Updated %s '%s'" ref-or-link search))\r
+          (setq message-text (format "Did not find %s '%s'" ref-or-link search))))))\r
+\r
+\r
+   ((memq what '(highlight unhighlight))\r
+\r
+    (let ((where "buffer"))\r
+      (save-excursion\r
+        (save-restriction\r
+          (when (and transient-mark-mode\r
+                     mark-active)\r
+            (narrow-to-region (region-beginning) (region-end))\r
+            (setq where "region"))\r
+\r
+          (if (eq what 'highlight)\r
+              (progn\r
+                (highlight-regexp ref-regex)\r
+                (setq message-text (format "Highlighted references in %s" where)))\r
+            (unhighlight-regexp ref-regex)\r
+            (setq message-text (format "Removed highlights for references in %s" where)))))))\r
+\r
+\r
+   ((memq what '(missing statistics))\r
+\r
+    (org-favtable--goto-top)\r
+    (let (missing \r
+          ref-field\r
+          ref\r
+          min\r
+          max \r
+          (total 0))\r
+\r
+      ;; start with list of all references\r
+      (setq missing (mapcar (lambda (x) (format "%s%d%s" head x tail)) \r
+                            (number-sequence 1 maxref)))\r
+\r
+      ;; go through table and remove all refs, that we see\r
+      (while (and (forward-line)\r
+                  (org-at-table-p))\r
+\r
+        ;; get ref-field and number\r
+        (setq ref-field (org-favtable--get-field 'ref))\r
+        (if (and ref-field \r
+                 (string-match ref-regex ref-field))\r
+            (setq ref (string-to-number (match-string 1 ref-field))))\r
+\r
+        ;; remove existing refs from list\r
+        (if ref-field (setq missing (delete ref-field missing)))\r
+\r
+        ;; record min and max            \r
+        (if (or (not min) (< ref min)) (setq min ref))\r
+        (if (or (not max) (> ref max)) (setq max ref))\r
+\r
+        ;; count\r
+        (setq total (1+ total)))\r
+\r
+      ;; insert them, if requested\r
+      (forward-line -1)\r
+      (if (eq what 'statistics)\r
+            \r
+          (setq message-text (format "Found %d references from %s to %s. %d references below highest do not appear in table. "\r
+                                     total \r
+                                     (format org-favtable--format min)   \r
+                                     (format org-favtable--format max)\r
+                                     (length missing)))\r
+\r
+        (if (y-or-n-p (format "Found %d missing references; do you wish to append them to the table of favorites" \r
+                              (length missing)))\r
+            (let (type)\r
+              (setq type (org-icompleting-read \r
+                          "Insert new lines for reuse by command \"new\" or just as missing ? " '("reuse" "missing")))\r
+              (mapc (lambda (x) \r
+                      (let (org-table-may-need-update) (org-table-insert-row t))\r
+                      (org-favtable--get-field 'ref x)\r
+                      (org-favtable--get-field 'count (format ":%s:" type)))\r
+                    missing)\r
+              (org-table-align)\r
+              (setq message-text (format "Inserted %d new lines for missing refernces" (length missing))))\r
+          (setq message-text (format "%d missing references." (length missing)))))))\r
+     \r
+      \r
+   (t (error "This is a bug: unmatched case '%s'" what)))\r
+\r
+\r
+  ;; remember what we have done for next time\r
+  (setq org-favtable--last-action what)\r
+    \r
+  ;; tell, what we have done and what can be yanked\r
+  (if kill-new-text (setq kill-new-text \r
+                          (substring-no-properties kill-new-text)))\r
+  (if (string= kill-new-text "") (setq kill-new-text nil))\r
+  (let ((m (concat \r
+            message-text\r
+            (if (and message-text kill-new-text) \r
+                " and r" \r
+              (if kill-new-text "R" ""))\r
+            (if kill-new-text (format "eady to yank '%s'" kill-new-text) ""))))\r
+    (unless (string= m "") (message m)))\r
+  (if kill-new-text (kill-new kill-new-text))))\r
+\r
+\r
+\r
+(defun org-favtable--parse-and-adjust-table ()\r
+\r
+  (let ((maxref 0)\r
+        top\r
+        bottom\r
+        ref-field\r
+        link-field\r
+        parts\r
+        numcols\r
+        head\r
+        tail\r
+        ref-regex\r
+        has-reuse\r
+        initial-point)\r
+\r
+    (setq initial-point (point))\r
+    (org-favtable--goto-top)\r
+    (setq top (point))\r
+    \r
+    (goto-char top)\r
+    \r
+    ;; count columns\r
+    (org-table-goto-column 100)\r
+    (setq numcols (- (org-table-current-column) 1))\r
+    \r
+    ;; get contents of columns\r
+    (forward-line -2)\r
+    (unless (org-at-table-p)\r
+      (org-favtable--report-setup-error \r
+       "Table of favorites starts with a hline" t))\r
+\r
+    ;; check for optional line consisting solely of width specifications\r
+    (beginning-of-line)\r
+    (if (looking-at "\\s *|\\(\\(\\s *|\\)\\|\\(\\s *<[0-9]+>\\s *|\\)\\)+\\s *$")\r
+        (forward-line -1))\r
+    (org-table-goto-column 1)\r
+\r
+    (setq org-favtable--columns (org-favtable--parse-headings numcols))\r
+    \r
+    ;; Go beyond end of table\r
+    (while (org-at-table-p) (forward-line 1))\r
+    \r
+    ;; Kill all empty rows at bottom\r
+    (while (progn\r
+             (forward-line -1)\r
+             (org-table-goto-column 1)\r
+             (and\r
+              (not (org-favtable--get-field 'ref))\r
+              (not (org-favtable--get-field 'link))))\r
+      (org-table-kill-row))\r
+    (forward-line)\r
+    (setq bottom (point))\r
+    (forward-line -1)\r
+    \r
+    ;; Retrieve any decorations around the number within the first nonempty ref-field\r
+    (goto-char top)\r
+    (while (and (org-at-table-p)\r
+                (not (setq ref-field (org-favtable--get-field 'ref))))\r
+      (forward-line))\r
+\r
+    ;; Some Checking\r
+    (unless ref-field\r
+      (org-favtable--report-setup-error \r
+       "No line of reference column contains a number" t))\r
+    \r
+    (unless (string-match "^\\([^0-9]*\\)\\([0-9]+\\)\\([^0-9]*\\)$" ref-field)\r
+      (org-favtable--report-setup-error \r
+       (format "First reference in table table of favorites ('%s') does not contain a number" ref-field) t))\r
+    \r
+\r
+    ;; These are the decorations used within the first row of favtable\r
+    (setq head (match-string 1 ref-field))\r
+    (setq tail (match-string 3 ref-field))\r
+    (setq ref-regex (concat (regexp-quote head)\r
+                            "\\([0-9]+\\)" \r
+                            (regexp-quote tail)))\r
+\r
+    ;; Go through table to find maximum number and do some checking\r
+    (let ((ref 0))\r
+\r
+      (while (org-at-table-p) \r
+\r
+        (setq ref-field (org-favtable--get-field 'ref))\r
+        (setq link-field (org-favtable--get-field 'link))\r
+\r
+        (if (and (not ref-field)\r
+                 (not link-field))\r
+            (throw 'content-error "Columns ref and link are both empty in this line"))\r
+\r
+        (if ref-field\r
+            (if (string-match ref-regex ref-field)\r
+                ;; grab number\r
+                (setq ref (string-to-number (match-string 1 ref-field)))\r
+              (throw 'content-error "Column ref does not contain a number")))\r
+\r
+        ;; check, if higher ref\r
+        (if (> ref maxref) (setq maxref ref))\r
+\r
+        ;; check if ref is ment for reuse\r
+        (if (string= (org-favtable--get-field 'count) ":reuse:")\r
+            (setq has-reuse 1))\r
+\r
+        (forward-line 1)))\r
+    \r
+    ;; sort used to be here\r
+    \r
+    (setq parts (list head maxref tail numcols ref-regex has-reuse))\r
+        \r
+    ;; go back to top of table\r
+    (goto-char top)\r
+\r
+    parts))\r
+\r
+\r
+\r
+(defun org-favtable--sort-table (sort-column)\r
+\r
+  (unless sort-column (setq sort-column (org-favtable--column-num 'sort)))\r
+\r
+  (let (top \r
+        bottom\r
+        ref-field\r
+        count-field\r
+        count-special)\r
+\r
+\r
+    ;; get boundaries of table\r
+    (org-favtable--goto-top)\r
+    (forward-line 0)\r
+    (setq top (point))\r
+    (while (org-at-table-p) (forward-line))\r
+    (setq bottom (point))\r
+    \r
+    (save-restriction\r
+      (narrow-to-region top bottom)\r
+      (goto-char top)\r
+      (sort-subr t\r
+                 'forward-line \r
+                 'end-of-line \r
+                 (lambda ()\r
+                   (let (ref\r
+                         (ref-field (or (org-favtable--get-field 'ref) ""))\r
+                         (count-field (or (org-favtable--get-field 'count) ""))\r
+                         (count-special 0))\r
+\r
+                     ;; get reference with leading zeroes, so it can be\r
+                     ;; sorted as text\r
+                     (string-match org-favtable--ref-regex ref-field)\r
+                     (setq ref (format \r
+                                "%06d" \r
+                                (string-to-number \r
+                                 (or (match-string 1 ref-field)\r
+                                     "0"))))\r
+\r
+                     ;; find out, if special token in count-column\r
+                     (setq count-special (format "%d" \r
+                                                 (- 2\r
+                                                    (length (member count-field '(":missing:" ":reuse:"))))))\r
+                   \r
+                     ;; Construct different sort-keys according to\r
+                     ;; requested sort column; prepend count-special to\r
+                     ;; sort special entries at bottom of table, append ref\r
+                     ;; as a secondary sort key\r
+                     (cond \r
+\r
+                      ((eq sort-column 'count)\r
+                       (concat count-special\r
+                               (format \r
+                                "%08d" \r
+                                (string-to-number (or (org-favtable--get-field 'count)\r
+                                                      ""))) \r
+                               ref))\r
+                    \r
+                      ((eq sort-column 'last-accessed)\r
+                       (concat count-special\r
+                               (org-favtable--get-field 'last-accessed) \r
+                               " " \r
+                               ref))\r
+                    \r
+                      ((eq sort-column 'ref)\r
+                       (concat count-special\r
+                               ref))\r
+                    \r
+                      (t (error "This is a bug: unmatched case '%s'" sort-column)))))\r
+               \r
+                 nil 'string<)))\r
+    \r
+  ;; align table\r
+  (org-table-align)) \r
+\r
+\r
+(defun org-favtable--goto-top ()\r
+\r
+  ;; go to heading of node\r
+  (while (not (org-at-heading-p)) (forward-line -1))\r
+  (forward-line 1)\r
+  ;; go to table within node, but make sure we do not get into another node\r
+  (while (and (not (org-at-heading-p))\r
+              (not (org-at-table-p))\r
+              (not (eq (point) (point-max)))) \r
+    (forward-line 1))\r
+     \r
+  ;; check, if there really is a table\r
+  (unless (org-at-table-p)\r
+    (org-favtable--report-setup-error \r
+     (format "Cannot find favtable within node %s" org-favtable-id) t))\r
+\r
+  ;; go to first hline\r
+  (while (and (not (org-at-table-hline-p))\r
+              (org-at-table-p))\r
+    (forward-line 1))\r
+     \r
+  ;; and check\r
+  (unless (org-at-table-hline-p)\r
+    (org-favtable--report-setup-error \r
+     "Cannot find hline within table of favorites" t))      \r
+\r
+  (forward-line 1)\r
+  (org-table-goto-column 1))\r
+\r
+\r
+\r
+(defun org-favtable--id-find ()\r
+  "Find org-favtable-id"\r
+  (let ((marker (org-id-find org-favtable-id 'marker))\r
+        marker-and-buffer)\r
+\r
+    (if marker \r
+        (progn \r
+          (setq marker-and-buffer (cons (marker-buffer marker) (marker-position marker)))\r
+          (move-marker marker nil)\r
+          marker-and-buffer)\r
+      nil)))\r
+\r
+\r
+\r
+(defun org-favtable--parse-headings (numcols)\r
+\r
+  (let (columns)\r
+\r
+    ;; Associate names of special columns with column-numbers\r
+    (setq columns (copy-tree '((ref . 0) (link . 0) (created . 0) (last-accessed . 0) \r
+                               (count . 0) (sort . nil) (copy . nil))))\r
+\r
+    ;; For each column\r
+    (dotimes (col numcols)\r
+      (let* (field-flags ;; raw heading, consisting of file name and maybe\r
+                         ;; flags (seperated by ";")\r
+             field       ;; field name only\r
+             field-symbol ;; and as a symbol\r
+             flags       ;; flags from field-flags\r
+             found)\r
+\r
+        ;; parse field-flags into field and flags\r
+        (setq field-flags (org-trim (org-table-get-field (+ col 1))))\r
+        (if (string-match "^\\([^;]*\\);\\([a-z]+\\)$" field-flags)\r
+            (progn \r
+              (setq field (downcase (or (match-string 1 field-flags) "")))\r
+              ;; get flags as list of characters\r
+              (setq flags (mapcar 'string-to-char \r
+                                  (split-string \r
+                                   (downcase (match-string 2 field-flags)) \r
+                                   "" t))))\r
+          ;; no flags\r
+          (setq field field-flags))\r
+\r
+        (unless (string= field "") (setq field-symbol (intern (downcase field))))\r
+\r
+        ;; Check, that no flags appear twice\r
+        (mapc (lambda (x)\r
+                (when (memq (car x) flags)\r
+                  (if (cdr (assoc (cdr x) columns))\r
+                      (org-favtable--report-setup-error \r
+                       (format "More than one heading is marked with flag '%c'" (car x)) t))))\r
+              '((?s . sort)\r
+                (?c . copy)))\r
+        \r
+        ;; Process flags\r
+        (if (memq ?s flags)\r
+            (setcdr (assoc 'sort columns) field-symbol))\r
+        (if (memq ?c flags)\r
+            (setcdr (assoc 'copy columns) (+ col 1)))\r
+        \r
+        ;; Store columns in alist\r
+        (setq found (assoc field-symbol columns))\r
+        (when found\r
+          (if (> (cdr found) 0) \r
+              (org-favtable--report-setup-error \r
+               (format "'%s' appears two times as column heading" (downcase field)) t))\r
+          (setcdr found (+ col 1)))))\r
+\r
+    ;; check if all necessary informations have been specified\r
+    (mapc (lambda (col) \r
+            (unless (> (cdr (assoc col columns)) 0)\r
+              (org-favtable--report-setup-error \r
+               (format "column '%s' has not been set" col) t)))\r
+          '(ref link count created last-accessed))\r
+\r
+    ;; use ref as a default sort-column\r
+    (unless (cdr (assoc 'sort columns))\r
+      (setcdr (assoc 'sort columns) 'ref))\r
+    columns))\r
+\r
+\r
+\r
+(defun org-favtable--report-setup-error (text &optional switch-to-node)\r
+\r
+  (when switch-to-node \r
+    (org-id-goto org-favtable-id)\r
+    (delete-other-windows))\r
+  \r
+  (when (y-or-n-p (concat\r
+                   text \r
+                   ";\n"\r
+                   "the correct setup is explained in the documentation of 'org-favtable-id'.\n" \r
+                   "Do you want to read it ? "))\r
+    (org-favtable--show-help 'org-favtable-id))\r
+\r
+  (error "")\r
+  (setq org-favtable--windowconfig-before nil)\r
+  (move-marker org-favtable--marker-outside-before nil)\r
+  (setq org-favtable--last-action 'leave))\r
+\r
+\r
+\r
+(defun org-favtable--show-help (function-or-variable)\r
+\r
+  (let ((isfun (functionp function-or-variable)))\r
+    ;; bring up help-buffer for function or variable\r
+    (if isfun\r
+        (describe-function function-or-variable)\r
+      (describe-variable function-or-variable))\r
+\r
+    \r
+    ;; clean up help-buffer\r
+    (pop-to-buffer "*Help*")\r
+    (let ((inhibit-read-only t)) \r
+      (goto-char (point-min))\r
+      (while (progn\r
+               (kill-line 1)\r
+               (not (looking-at \r
+                     (if isfun\r
+                         "(" \r
+                       "Documentation:")))))\r
+      (kill-line (if isfun 2 3))\r
+      (goto-char (point-max))\r
+      (kill-line -2)\r
+      (goto-char (point-min)))))\r
+                 \r
+\r
+\r
+(defun org-favtable--update-line (ref-or-link)\r
+\r
+  (let (initial\r
+        found\r
+        count-field\r
+        (ref-node-buffer-and-point (org-favtable--id-find)))\r
+\r
+    (with-current-buffer (car ref-node-buffer-and-point)\r
+      \r
+      ;; search reference or link, if given (or assume, that we are already positioned right)\r
+      (when ref-or-link\r
+        (setq initial (point))\r
+        (goto-char (cdr ref-node-buffer-and-point))\r
+        (org-favtable--goto-top)\r
+        (while (and (org-at-table-p)\r
+                    (not (or (string= ref-or-link (org-favtable--get-field 'ref))\r
+                             (string= ref-or-link (org-favtable--get-field 'link)))))\r
+          (forward-line)))\r
+      \r
+      (if (not (org-at-table-p))\r
+          (error "Did not find reference or link '%s'" ref-or-link)\r
+        (setq count-field (org-favtable--get-field 'count))\r
+\r
+        ;; update count field only if number or empty; leave :missing: and :reuse: as is\r
+        (if (or (not count-field)\r
+                (string-match "^[0-9]+$" count-field))\r
+            (org-favtable--get-field 'count\r
+                                    (number-to-string \r
+                                     (+ 1 (string-to-number (or count-field "0"))))))\r
+\r
+        ;; update timestamp\r
+        (org-table-goto-column (org-favtable--column-num 'last-accessed))\r
+        (org-table-blank-field)\r
+        (org-insert-time-stamp nil t t)\r
+\r
+        (setq found t))\r
+      \r
+      (if initial (goto-char initial))\r
+      \r
+      found)))\r
+\r
+\r
+\r
+(defun org-favtable--occur-helper (action)\r
+  (let ((line-beg (line-beginning-position))\r
+        key search link ref)\r
+\r
+    ;; extract reference or link from text property (as put there before)\r
+    (setq ref (get-text-property line-beg 'org-favtable--ref))\r
+    (if (string= ref "") (setq ref nil))\r
+    (setq link (get-text-property line-beg 'org-favtable--link))\r
+    (if (string= link "") (setq link nil))\r
+      \r
+    (org-favtable action \r
+                  (or link ref) ;; prefer link\r
+                  (if link t nil))))\r
+\r
+\r
+(defun org-favtable--get-field (key &optional value)\r
+  (let (field)\r
+    (setq field (org-trim (org-table-get-field (cdr (assoc key org-favtable--columns)) value)))\r
+    (if (string= field "") (setq field nil))\r
+    \r
+    field))\r
+\r
+\r
+(defun org-favtable--column-num (key)\r
+  (cdr (assoc key org-favtable--columns)))\r
+\r
+\r
+(defun org-favtable-version ()\r
+  "Show version of org-favtable"\r
+  (message "org-favtable %s" org-favtable--version))\r
+\r
+\r
+(defun org-favtable--make-guarded-search (ref &optional dont-quote)\r
+  (concat "\\b" (if dont-quote ref (regexp-quote ref)) "\\b"))\r
+\r
+\r
+(provide 'org-favtable)\r
+\r
+;; Local Variables:\r
+;; fill-column: 75\r
+;; comment-column: 50\r
+;; End:\r
+\r
+;;; org-favtable.el ends here\r