(list-colors-display): Use with-help-window instead of with-output-to-temp-buffer.
[bpt/emacs.git] / lisp / allout.el
index a64ba4b..8878c56 100644 (file)
@@ -1,18 +1,20 @@
 ;;; allout.el --- extensive outline mode for use alone and with other modes
 
 ;; Copyright (C) 1992, 1993, 1994, 2001, 2002, 2003, 2004,
-;;   2005 Free Software Foundation, Inc.
+;;   2005, 2006, 2007 Free Software Foundation, Inc.
 
-;; Author: Ken Manheimer <klm@zope.com>
-;; Maintainer: Ken Manheimer <klm@zope.com>
+;; Author: Ken Manheimer <ken dot manheimer at gmail dot com>
+;; Maintainer: Ken Manheimer <ken dot manheimer at gmail dot com>
 ;; Created: Dec 1991 - first release to usenet
-;; Keywords: outlines mode wp languages
+;; Version: 2.2.1
+;; Keywords: outlines wp languages
+;; Website: http://myriadicity.net/Sundry/EmacsAllout
 
 ;; This file is part of GNU Emacs.
 
 ;; GNU Emacs is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
+;; the Free Software Foundation; either version 3, or (at your option)
 ;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 
 ;;; Commentary:
 
-;; Allout outline mode provides extensive outline formatting and
-;; and manipulation beyond standard emacs outline mode.  It provides
-;; for structured editing of outlines, as well as navigation and
-;; exposure.  It also provides for syntax-sensitive text like
-;; programming languages.  (For an example, see the allout code
-;; itself, which is organized in ;; an outline framework.)
+;; Allout outline minor mode provides extensive outline formatting and
+;; and manipulation beyond standard emacs outline mode.  Some features:
 ;;
-;; In addition to outline navigation and exposure, allout includes:
-;;
-;;  - topic-oriented repositioning, cut, and paste
-;;  - integral outline exposure-layout
-;;  - incremental search with dynamic exposure and reconcealment of hidden text
-;;  - automatic topic-number maintenance
+;;  - Classic outline-mode topic-oriented navigation and exposure adjustment
+;;  - Topic-oriented editing including coherent topic and subtopic
+;;    creation, promotion, demotion, cut/paste across depths, etc.
+;;  - Incremental search with dynamic exposure and reconcealment of text
+;;  - Customizable bullet format - enables programming-language specific
+;;    outlining, for code-folding editing.  (Allout code itself is to try it;
+;;    formatted as an outline - do ESC-x eval-buffer in allout.el; but
+;;    emacs local file variables need to be enabled when the
+;;    file was visited - see `enable-local-variables'.)
+;;  - Configurable per-file initial exposure settings
+;;  - Symmetric-key and key-pair topic encryption, plus symmetric passphrase
+;;    mnemonic support, with verification against an established passphrase
+;;    (using a stashed encrypted dummy string) and user-supplied hint
+;;    maintenance.  (See allout-toggle-current-subtree-encryption docstring.
+;;    Currently only GnuPG encryption is supported, and integration
+;;    with gpg-agent is not yet implemented.)
+;;  - Automatic topic-number maintenance
 ;;  - "Hot-spot" operation, for single-keystroke maneuvering and
-;;    exposure control.  (See the `allout-mode' docstring.)
+;;    exposure control (see the allout-mode docstring)
+;;  - Easy rendering of exposed portions into numbered, latex, indented, etc
+;;    outline styles
+;;  - Careful attention to whitespace - enabling blank lines between items
+;;    and maintenance of hanging indentation (in paragraph auto-fill and
+;;    across topic promotion and demotion) of topic bodies consistent with
+;;    indentation of their topic header.
+;;
+;; and more.
+;;
+;; See the `allout-mode' function's docstring for an introduction to the
+;; mode.
 ;;
-;; and many other features.
+;; The latest development version and helpful notes are available at
+;; http://myriadicity.net/Sundry/EmacsAllout .
 ;;
 ;; The outline menubar additions provide quick reference to many of
-;; the features, and see the docstring of the function `allout-init'
+;; the features, and see the docstring of the variable `allout-init'
 ;; for instructions on priming your Emacs session for automatic
-;; activation of `allout-mode'.
+;; activation of allout-mode.
 ;;
 ;; See the docstring of the variables `allout-layout' and
 ;; `allout-auto-activation' for details on automatic activation of
-;; allout `allout-mode' as a minor mode.  (It has changed since allout
+;; `allout-mode' as a minor mode.  (It has changed since allout
 ;; 3.x, for those of you that depend on the old method.)
 ;;
 ;; Note - the lines beginning with `;;;_' are outline topic headers.
-;;        Just `ESC-x eval-current-buffer' to give it a whirl.
+;;        Just `ESC-x eval-buffer' to give it a whirl.
 
-;; Ken Manheimer       klm@zope.com
+;; ken manheimer (ken dot manheimer at gmail dot com)
 
 ;;; Code:
 
-;;;_* Provide
-(provide 'allout)
+;;;_* Dependency autoloads
+(require 'overlay)
+(eval-when-compile
+  ;; Most of the requires here are for stuff covered by autoloads.
+  ;; Since just byte-compiling doesn't trigger autoloads, so that
+  ;; "function not found" warnings would occur without these requires.
+  (progn
+    (require 'pgg)
+    (require 'pgg-gpg)
+    (require 'overlay)
+    ;; `cl' is required for `assert'.  `assert' is not covered by a standard
+    ;; autoload, but it is a macro, so that eval-when-compile is sufficient
+    ;; to byte-compile it in, or to do the require when the buffer evalled.
+    (require 'cl)
+    ))
 
 ;;;_* USER CUSTOMIZATION VARIABLES:
+
+;;;_ > defgroup allout
 (defgroup allout nil
   "Extensive outline mode for use alone and with other modes."
   :prefix "allout-"
-  :group 'editing
-  :version "22.1")
+  :group 'outlines)
 
 ;;;_ + Layout, Mode, and Topic Header Configuration
 
+;;;_  = allout-command-prefix
+(defcustom allout-command-prefix "\C-c "
+  "*Key sequence to be used as prefix for outline mode command key bindings.
+
+Default is '\C-c<space>'; just '\C-c' is more short-and-sweet, if you're
+willing to let allout use a bunch of \C-c keybindings."
+  :type 'string
+  :group 'allout)
+;;;_  = allout-keybindings-list
+;;; You have to reactivate allout-mode - `(allout-mode t)' - to
+;;; institute changes to this var.
+(defvar allout-keybindings-list ()
+  "*List of `allout-mode' key / function bindings, for `allout-mode-map'.
+
+String or vector key will be prefaced with `allout-command-prefix',
+unless optional third, non-nil element is present.")
+(setq allout-keybindings-list
+      '(
+                                        ; Motion commands:
+        ("\C-n" allout-next-visible-heading)
+        ("\C-p" allout-previous-visible-heading)
+        ("\C-u" allout-up-current-level)
+        ("\C-f" allout-forward-current-level)
+        ("\C-b" allout-backward-current-level)
+        ("\C-a" allout-beginning-of-current-entry)
+        ("\C-e" allout-end-of-entry)
+                                        ; Exposure commands:
+        ("\C-i" allout-show-children)
+        ("\C-s" allout-show-current-subtree)
+        ("\C-h" allout-hide-current-subtree)
+        ("\C-t" allout-toggle-current-subtree-exposure)
+        ("h" allout-hide-current-subtree)
+        ("\C-o" allout-show-current-entry)
+        ("!" allout-show-all)
+        ("x" allout-toggle-current-subtree-encryption)
+                                        ; Alteration commands:
+        (" " allout-open-sibtopic)
+        ("." allout-open-subtopic)
+        ("," allout-open-supertopic)
+        ("'" allout-shift-in)
+        (">" allout-shift-in)
+        ("<" allout-shift-out)
+        ("\C-m" allout-rebullet-topic)
+        ("*" allout-rebullet-current-heading)
+        ("#" allout-number-siblings)
+        ("\C-k" allout-kill-line t)
+        ("\M-k" allout-copy-line-as-kill t)
+        ("\C-y" allout-yank t)
+        ("\M-y" allout-yank-pop t)
+        ("\C-k" allout-kill-topic)
+        ("\M-k" allout-copy-topic-as-kill)
+                                        ; Miscellaneous commands:
+       ;([?\C-\ ] allout-mark-topic)
+        ("@" allout-resolve-xref)
+        ("=c" allout-copy-exposed-to-buffer)
+        ("=i" allout-indented-exposed-to-buffer)
+       ("=t" allout-latexify-exposed)
+       ("=p" allout-flatten-exposed-to-buffer)))
+
 ;;;_  = allout-auto-activation
 (defcustom allout-auto-activation nil
   "*Regulates auto-activation modality of allout outlines - see `allout-init'.
@@ -103,38 +197,120 @@ this variable."
                 (const :tag "Mode only" "activate")
                 (const :tag "Off" nil))
   :group 'allout)
-;;;_  = allout-layout
-(defvar allout-layout nil
-  "*Layout specification and provisional mode trigger for allout outlines.
+;;;_  = allout-default-layout
+(defcustom allout-default-layout '(-2 : 0)
+  "*Default allout outline layout specification.
+
+This setting specifies the outline exposure to use when
+`allout-layout' has the local value `t'.  This docstring describes the
+layout specifications.
+
+A list value specifies a default layout for the current buffer,
+to be applied upon activation of `allout-mode'.  Any non-nil
+value will automatically trigger `allout-mode', provided
+`allout-init' has been called to enable this behavior.
+
+The types of elements in the layout specification are:
+
+ integer - dictate the relative depth to open the corresponding topic(s),
+           where:
+         - negative numbers force the topic to be closed before opening
+           to the absolute value of the number, so all siblings are open
+           only to that level.
+         - positive numbers open to the relative depth indicated by the
+           number, but do not force already opened subtopics to be closed.
+         - 0 means to close topic - hide all subitems.
+ :   - repeat spec - apply the preceeding element to all siblings at
+       current level, *up to* those siblings that would be covered by specs
+       following the `:' on the list.  Ie, apply to all topics at level but
+       trailing ones accounted for by trailing specs.  (Only the first of
+       multiple colons at the same level is honored - later ones are ignored.)
+ *   - completely exposes the topic, including bodies
+ +   - exposes all subtopics, but not the bodies
+ -   - exposes the body of the corresponding topic, but not subtopics
+ list - a nested layout spec, to be applied intricately to its
+        corresponding item(s)
 
-Buffer-specific.
+Examples:
+ '(-2 : 0)
+       Collapse the top-level topics to show their children and
+        grandchildren, but completely collapse the final top-level topic.
+ '(-1 () : 1 0)
+       Close the first topic so only the immediate subtopics are shown,
+        leave the subsequent topics exposed as they are until the second
+       second to last topic, which is exposed at least one level, and
+        completely close the last topic.
+ '(-2 : -1 *)
+        Expose children and grandchildren of all topics at current
+       level except the last two; expose children of the second to
+       last and completely expose the last one, including its subtopics.
 
-A list value specifies a default layout for the current buffer, to be
-applied upon activation of `allout-mode'.  Any non-nil value will
-automatically trigger `allout-mode', provided `allout-init'
-has been called to enable it.
+See `allout-expose-topic' for more about the exposure process.
 
-See the docstring for `allout-init' for details on setting up for
-auto-mode-activation, and for `allout-expose-topic' for the format of
-the layout specification.
+Also, allout's mode-specific provisions will make topic prefixes default
+to the comment-start string, if any, of the language of the file.  This
+is modulo the setting of `allout-use-mode-specific-leader', which see."
+  :type 'allout-layout-type
+  :group 'allout)
+;;;_  : allout-layout-type
+(define-widget 'allout-layout-type 'lazy
+  "Allout layout format customization basic building blocks."
+  :type '(repeat
+          (choice (integer :tag "integer (<= zero is strict)")
+                  (const :tag ": (repeat prior)" :)
+                  (const :tag "* (completely expose)" *)
+                  (const :tag "+ (expose all offspring, headlines only)" +)
+                  (const :tag "- (expose topic body but not offspring)" -)
+                  (allout-layout-type :tag "<Nested layout>"))))
+
+;;;_  = allout-inhibit-auto-fill
+(defcustom allout-inhibit-auto-fill nil
+  "*If non-nil, auto-fill will be inhibited in the allout buffers.
+
+You can customize this setting to set it for all allout buffers, or set it
+in individual buffers if you want to inhibit auto-fill only in particular
+buffers.  (You could use a function on `allout-mode-hook' to inhibit
+auto-fill according, eg, to the major mode.)
+
+If you don't set this and auto-fill-mode is enabled, allout will use the
+value that `normal-auto-fill-function', if any, when allout mode starts, or
+else allout's special hanging-indent maintaining auto-fill function,
+`allout-auto-fill'."
+  :type 'boolean
+  :group 'allout)
+(make-variable-buffer-local 'allout-inhibit-auto-fill)
+;;;_  = allout-use-hanging-indents
+(defcustom allout-use-hanging-indents t
+  "*If non-nil, topic body text auto-indent defaults to indent of the header.
+Ie, it is indented to be just past the header prefix.  This is
+relevant mostly for use with indented-text-mode, or other situations
+where auto-fill occurs."
+  :type 'boolean
+  :group 'allout)
+(make-variable-buffer-local 'allout-use-hanging-indents)
+;;;###autoload
+(put 'allout-use-hanging-indents 'safe-local-variable
+     (if (fboundp 'booleanp) 'booleanp '(lambda (x) (member x '(t nil)))))
+;;;_  = allout-reindent-bodies
+(defcustom allout-reindent-bodies (if allout-use-hanging-indents
+                                   'text)
+  "*Non-nil enables auto-adjust of topic body hanging indent with depth shifts.
 
-You can associate a particular outline layout with a file by setting
-this var via the file's local variables.  For example, the following
-lines at the bottom of an Emacs Lisp file:
+When active, topic body lines that are indented even with or beyond
+their topic header are reindented to correspond with depth shifts of
+the header.
 
-;;;Local variables:
-;;;allout-layout: \(0 : -1 -1 0)
-;;;End:
+A value of t enables reindent in non-programming-code buffers, ie
+those that do not have the variable `comment-start' set.  A value of
+`force' enables reindent whether or not `comment-start' is set."
+  :type '(choice (const nil) (const t) (const text) (const force))
+  :group 'allout)
 
-will, modulo the above-mentioned conditions, cause the mode to be
-activated when the file is visited, followed by the equivalent of
-`\(allout-expose-topic 0 : -1 -1 0)'.  \(This is the layout used for
-the allout.el, itself.)
+(make-variable-buffer-local 'allout-reindent-bodies)
+;;;###autoload
+(put 'allout-reindent-bodies 'safe-local-variable
+     '(lambda (x) (memq x '(nil t text force))))
 
-Also, allout's mode-specific provisions will make topic prefixes default
-to the comment-start string, if any, of the language of the file.  This
-is modulo the setting of `allout-use-mode-specific-leader', which see.")
-(make-variable-buffer-local 'allout-layout)
 ;;;_  = allout-show-bodies
 (defcustom allout-show-bodies nil
   "*If non-nil, show entire body when exposing a topic, rather than
@@ -142,19 +318,82 @@ just the header."
   :type 'boolean
   :group 'allout)
 (make-variable-buffer-local 'allout-show-bodies)
+;;;###autoload
+(put 'allout-show-bodies 'safe-local-variable
+     (if (fboundp 'booleanp) 'booleanp '(lambda (x) (member x '(t nil)))))
+
+;;;_  = allout-beginning-of-line-cycles
+(defcustom allout-beginning-of-line-cycles t
+  "*If non-nil, \\[allout-beginning-of-line] will cycle through smart-placement options.
+
+Cycling only happens on when the command is repeated, not when it
+follows a different command.
+
+Smart-placement means that repeated calls to this function will
+advance as follows:
+
+ - if the cursor is on a non-headline body line and not on the first column:
+   then it goes to the first column
+ - if the cursor is on the first column of a non-headline body line:
+   then it goes to the start of the headline within the item body
+ - if the cursor is on the headline and not the start of the headline:
+   then it goes to the start of the headline
+ - if the cursor is on the start of the headline:
+   then it goes to the bullet character (for hotspot navigation)
+ - if the cursor is on the bullet character:
+   then it goes to the first column of that line (the headline)
+ - if the cursor is on the first column of the headline:
+   then it goes to the start of the headline within the item body.
+
+In this fashion, you can use the beginning-of-line command to do
+its normal job and then, when repeated, advance through the
+entry, cycling back to start.
+
+If this configuration variable is nil, then the cursor is just
+advanced to the beginning of the line and remains there on
+repeated calls."
+  :type 'boolean :group 'allout)
+;;;_  = allout-end-of-line-cycles
+(defcustom allout-end-of-line-cycles t
+  "*If non-nil, \\[allout-end-of-line] will cycle through smart-placement options.
+
+Cycling only happens on when the command is repeated, not when it
+follows a different command.
+
+Smart-placement means that repeated calls to this function will
+advance as follows:
+
+ - if the cursor is not on the end-of-line,
+   then it goes to the end-of-line
+ - if the cursor is on the end-of-line but not the end-of-entry,
+   then it goes to the end-of-entry, exposing it if necessary
+ - if the cursor is on the end-of-entry,
+   then it goes to the end of the head line
+
+In this fashion, you can use the end-of-line command to do its
+normal job and then, when repeated, advance through the entry,
+cycling back to start.
+
+If this configuration variable is nil, then the cursor is just
+advanced to the end of the line and remains there on repeated
+calls."
+  :type 'boolean :group 'allout)
 
 ;;;_  = allout-header-prefix
 (defcustom allout-header-prefix "."
+;; this string is treated as literal match.  it will be `regexp-quote'd, so
+;; one cannot use regular expressions to match varying header prefixes.
   "*Leading string which helps distinguish topic headers.
 
 Outline topic header lines are identified by a leading topic
 header prefix, which mostly have the value of this var at their front.
-\(Level 1 topics are exceptions.  They consist of only a single
-character, which is typically set to the `allout-primary-bullet'.  Many
-outlines start at level 2 to avoid this discrepancy."
+Level 1 topics are exceptions.  They consist of only a single
+character, which is typically set to the `allout-primary-bullet'."
   :type 'string
   :group 'allout)
 (make-variable-buffer-local 'allout-header-prefix)
+;;;###autoload
+(put 'allout-header-prefix 'safe-local-variable 'stringp)
 ;;;_  = allout-primary-bullet
 (defcustom allout-primary-bullet "*"
   "Bullet used for top-level outline topics.
@@ -170,8 +409,10 @@ bullets."
   :type 'string
   :group 'allout)
 (make-variable-buffer-local 'allout-primary-bullet)
+;;;###autoload
+(put 'allout-primary-bullet 'safe-local-variable 'stringp)
 ;;;_  = allout-plain-bullets-string
-(defcustom allout-plain-bullets-string ".:,;"
+(defcustom allout-plain-bullets-string ".,"
   "*The bullets normally used in outline topic prefixes.
 
 See `allout-distinctive-bullets-string' for the other kind of
@@ -184,80 +425,107 @@ of this var to take effect."
   :type 'string
   :group 'allout)
 (make-variable-buffer-local 'allout-plain-bullets-string)
+;;;###autoload
+(put 'allout-plain-bullets-string 'safe-local-variable 'stringp)
 ;;;_  = allout-distinctive-bullets-string
-(defcustom allout-distinctive-bullets-string "*+-=>([{}&!?#%\"X@$~_\\"
+(defcustom allout-distinctive-bullets-string "*+-=>()[{}&!?#%\"X@$~_\\:;^"
   "*Persistent outline header bullets used to distinguish special topics.
 
-These bullets are used to distinguish topics from the run-of-the-mill
-ones.  They are not used in the standard topic headers created by
-the topic-opening, shifting, and rebulleting \(eg, on topic shift,
-topic paste, blanket rebulleting) routines, but are offered among the
-choices for rebulleting.  They are not altered by the above automatic
-rebulleting, so they can be used to characterize topics, eg:
+These bullets are distinguish topics with particular character.
+They are not used by default in the topic creation routines, but
+are offered as options when you modify topic creation with a
+universal argument \(\\[universal-argument]), or during rebulleting \(\\[allout-rebullet-current-heading]).
+
+Distinctive bullets are not cycled when topics are shifted or
+otherwise automatically rebulleted, so their marking is
+persistent until deliberately changed.  Their significance is
+purely by convention, however.  Some conventions suggest
+themselves:
+
+ `(' - open paren - an aside or incidental point
+ `?' - question mark - uncertain or outright question
+ `!' - exclamation point/bang - emphatic
+ `[' - open square bracket - meta-note, about item instead of item's subject
+ `\"' - double quote - a quotation or other citation
+ `=' - equal sign - an assignement, equating a name with some connotation
+ `^' - carat - relates to something above
+
+Some are more elusive, but their rationale may be recognizable:
 
- `?' question topics
- `\(' parenthetic comment \(with a matching close paren inside)
- `[' meta-note \(with a matching close ] inside)
- `\"' a quote
- `=' value settings
- `~' \"more or less\"
+ `+' - plus - pending consideration, completion
+ `_' - underscore - done, completed
+ `&' - ampersand - addendum, furthermore
 
-... just for example.  (`#' typically has a special meaning to the
-software, according to the value of `allout-numbered-bullet'.)
+\(Some other non-plain bullets have special meaning to the
+software.  By default:
 
-See `allout-plain-bullets-string' for the selection of
-alternating bullets.
+ `~' marks encryptable topics - see `allout-topic-encryption-bullet'
+ `#' marks auto-numbered bullets - see `allout-numbered-bullet'.)
+
+See `allout-plain-bullets-string' for the standard, alternating
+bullets.
 
 You must run `set-allout-regexp' in order for outline mode to
-reconcile to changes of this value.
+adopt changes of this value.
 
 DO NOT include the close-square-bracket, `]', on either of the bullet
 strings."
   :type 'string
   :group 'allout)
 (make-variable-buffer-local 'allout-distinctive-bullets-string)
+;;;###autoload
+(put 'allout-distinctive-bullets-string 'safe-local-variable 'stringp)
 
 ;;;_  = allout-use-mode-specific-leader
 (defcustom allout-use-mode-specific-leader t
   "*When non-nil, use mode-specific topic-header prefixes.
 
-Allout outline mode will use the mode-specific `allout-mode-leaders'
-and/or comment-start string, if any, to lead the topic prefix string,
-so topic headers look like comments in the programming language.
+Allout outline mode will use the mode-specific `allout-mode-leaders' or
+comment-start string, if any, to lead the topic prefix string, so topic
+headers look like comments in the programming language.  It will also use
+the comment-start string, with an '_' appended, for `allout-primary-bullet'.
 
-String values are used as they stand.
+String values are used as literals, not regular expressions, so
+do not escape any regulare-expression characters.
 
 Value t means to first check for assoc value in `allout-mode-leaders'
-alist, then use comment-start string, if any, then use default \(`.').
+alist, then use comment-start string, if any, then use default (`.').
 \(See note about use of comment-start strings, below.)
 
 Set to the symbol for either of `allout-mode-leaders' or
 `comment-start' to use only one of them, respectively.
 
-Value nil means to always use the default \(`.').
-
-comment-start strings that do not end in spaces are tripled, and an
-`_' underscore is tacked on the end, to distinguish them from regular
-comment strings.  comment-start strings that do end in spaces are not
-tripled, but an underscore is substituted for the space. [This
-presumes that the space is for appearance, not comment syntax.  You
-can use `allout-mode-leaders' to override this behavior, when
-incorrect.]"
+Value nil means to always use the default (`.') and leave
+`allout-primary-bullet' unaltered.
+
+comment-start strings that do not end in spaces are tripled in
+the header-prefix, and an `_' underscore is tacked on the end, to
+distinguish them from regular comment strings.  comment-start
+strings that do end in spaces are not tripled, but an underscore
+is substituted for the space. [This presumes that the space is
+for appearance, not comment syntax.  You can use
+`allout-mode-leaders' to override this behavior, when
+undesired.]"
   :type '(choice (const t) (const nil) string
                 (const allout-mode-leaders)
                 (const comment-start))
   :group 'allout)
+;;;###autoload
+(put 'allout-use-mode-specific-leader 'safe-local-variable
+     '(lambda (x) (or (memq x '(t nil allout-mode-leaders comment-start))
+                      (stringp x))))
 ;;;_  = allout-mode-leaders
 (defvar allout-mode-leaders '()
   "Specific allout-prefix leading strings per major modes.
 
-Entries will be used instead or in lieu of mode-specific
-comment-start strings.  See also `allout-use-mode-specific-leader'.
+Use this if the mode's comment-start string isn't what you
+prefer, or if the mode lacks a comment-start string.  See
+`allout-use-mode-specific-leader' for more details.
 
 If you're constructing a string that will comment-out outline
 structuring so it can be included in program code, append an extra
 character, like an \"_\" underscore, to distinguish the lead string
-from regular comments that start at bol.")
+from regular comments that start at the beginning-of-line.")
 
 ;;;_  = allout-old-style-prefixes
 (defcustom allout-old-style-prefixes nil
@@ -272,6 +540,9 @@ are always respected by the topic maneuvering functions."
   :type 'boolean
   :group 'allout)
 (make-variable-buffer-local 'allout-old-style-prefixes)
+;;;###autoload
+(put 'allout-old-style-prefixes 'safe-local-variable
+     (if (fboundp 'booleanp) 'booleanp '(lambda (x) (member x '(t nil)))))
 ;;;_  = allout-stylish-prefixes - alternating bullets
 (defcustom allout-stylish-prefixes t
   "*Do fancy stuff with topic prefix bullets according to level, etc.
@@ -318,6 +589,9 @@ is non-nil."
   :type 'boolean
   :group 'allout)
 (make-variable-buffer-local 'allout-stylish-prefixes)
+;;;###autoload
+(put 'allout-stylish-prefixes 'safe-local-variable
+     (if (fboundp 'booleanp) 'booleanp '(lambda (x) (member x '(t nil)))))
 
 ;;;_  = allout-numbered-bullet
 (defcustom allout-numbered-bullet "#"
@@ -330,6 +604,11 @@ disables numbering maintenance."
   :type '(choice (const nil) string)
   :group 'allout)
 (make-variable-buffer-local 'allout-numbered-bullet)
+;;;###autoload
+(put 'allout-numbered-bullet 'safe-local-variable
+     (if (fboundp 'string-or-null-p)
+         'string-or-null-p
+       '(lambda (x) (or (stringp x) (null x)))))
 ;;;_  = allout-file-xref-bullet
 (defcustom allout-file-xref-bullet "@"
   "*Bullet signifying file cross-references, for `allout-resolve-xref'.
@@ -337,7 +616,11 @@ disables numbering maintenance."
 Set this var to the bullet you want to use for file cross-references."
   :type '(choice (const nil) string)
   :group 'allout)
-
+;;;###autoload
+(put 'allout-file-xref-bullet 'safe-local-variable
+     (if (fboundp 'string-or-null-p)
+         'string-or-null-p
+       '(lambda (x) (or (stringp x) (null x)))))
 ;;;_  = allout-presentation-padding
 (defcustom allout-presentation-padding 2
   "*Presentation-format white-space padding factor, for greater indent."
@@ -345,6 +628,8 @@ Set this var to the bullet you want to use for file cross-references."
   :group 'allout)
 
 (make-variable-buffer-local 'allout-presentation-padding)
+;;;###autoload
+(put 'allout-presentation-padding 'safe-local-variable 'integerp)
 
 ;;;_  = allout-abbreviate-flattened-numbering
 (defcustom allout-abbreviate-flattened-numbering nil
@@ -381,7 +666,7 @@ numbers are always used."
   :type 'string
   :group 'allout)
 ;;;_  - allout-title
-(defcustom allout-title '(or buffer-file-name (current-buffer-name))
+(defcustom allout-title '(or buffer-file-name (buffer-name))
   "*Expression to be evaluated to determine the title for LaTeX
 formatted copy."
   :type 'sexp
@@ -397,125 +682,121 @@ formatted copy."
   :type 'string
   :group 'allout)
 
-;;;_ + Miscellaneous customization
-
-;;;_  = allout-command-prefix
-(defcustom allout-command-prefix "\C-c"
-  "*Key sequence to be used as prefix for outline mode command key bindings."
-  :type 'string
+;;;_ + Topic encryption
+;;;_  = allout-encryption group
+(defgroup allout-encryption nil
+  "Settings for topic encryption features of allout outliner."
   :group 'allout)
-
-;;;_  = allout-keybindings-list
-;;; You have to reactivate allout-mode - `(allout-mode t)' - to
-;;; institute changes to this var.
-(defvar allout-keybindings-list ()
-  "*List of `allout-mode' key / function bindings, for `allout-mode-map'.
-
-String or vector key will be prefaced with `allout-command-prefix',
-unless optional third, non-nil element is present.")
-(setq allout-keybindings-list
-      '(
-                                        ; Motion commands:
-        ("\C-n" allout-next-visible-heading)
-        ("\C-p" allout-previous-visible-heading)
-        ("\C-u" allout-up-current-level)
-        ("\C-f" allout-forward-current-level)
-        ("\C-b" allout-backward-current-level)
-        ("\C-a" allout-beginning-of-current-entry)
-        ("\C-e" allout-end-of-current-entry)
-                                        ; Exposure commands:
-        ("\C-i" allout-show-children)
-        ("\C-s" allout-show-current-subtree)
-        ("\C-h" allout-hide-current-subtree)
-        ("\C-o" allout-show-current-entry)
-        ("!" allout-show-all)
-                                        ; Alteration commands:
-        (" " allout-open-sibtopic)
-        ("." allout-open-subtopic)
-        ("," allout-open-supertopic)
-        ("'" allout-shift-in)
-        (">" allout-shift-in)
-        ("<" allout-shift-out)
-        ("\C-m" allout-rebullet-topic)
-        ("*" allout-rebullet-current-heading)
-        ("#" allout-number-siblings)
-        ("\C-k" allout-kill-line t)
-        ("\C-y" allout-yank t)
-        ("\M-y" allout-yank-pop t)
-        ("\C-k" allout-kill-topic)
-                                        ; Miscellaneous commands:
-       ;([?\C-\ ] allout-mark-topic)
-        ("@" allout-resolve-xref)
-        ("=c" allout-copy-exposed-to-buffer)
-        ("=i" allout-indented-exposed-to-buffer)
-       ("=t" allout-latexify-exposed)
-       ("=p" allout-flatten-exposed-to-buffer)))
-
-;;;_  = allout-isearch-dynamic-expose
-(defcustom allout-isearch-dynamic-expose t
-  "*Non-nil enable dynamic exposure of hidden incremental-search
-targets as they're encountered."
+;;;_  = allout-topic-encryption-bullet
+(defcustom allout-topic-encryption-bullet "~"
+  "*Bullet signifying encryption of the entry's body."
+  :type '(choice (const nil) string)
+  :version "22.0"
+  :group 'allout-encryption)
+;;;_  = allout-passphrase-verifier-handling
+(defcustom allout-passphrase-verifier-handling t
+  "*Enable use of symmetric encryption passphrase verifier if non-nil.
+
+See the docstring for the `allout-enable-file-variable-adjustment'
+variable for details about allout ajustment of file variables."
   :type 'boolean
+  :version "22.0"
+  :group 'allout-encryption)
+(make-variable-buffer-local 'allout-passphrase-verifier-handling)
+;;;_  = allout-passphrase-hint-handling
+(defcustom allout-passphrase-hint-handling 'always
+  "*Dictate outline encryption passphrase reminder handling:
+
+ always - always show reminder when prompting
+ needed - show reminder on passphrase entry failure
+ disabled - never present or adjust reminder
+
+See the docstring for the `allout-enable-file-variable-adjustment'
+variable for details about allout ajustment of file variables."
+  :type '(choice (const always)
+                 (const needed)
+                 (const disabled))
+  :version "22.0"
+  :group 'allout-encryption)
+(make-variable-buffer-local 'allout-passphrase-hint-handling)
+;;;_  = allout-encrypt-unencrypted-on-saves
+(defcustom allout-encrypt-unencrypted-on-saves t
+  "*When saving, should topics pending encryption be encrypted?
+
+The idea is to prevent file-system exposure of any un-encrypted stuff, and
+mostly covers both deliberate file writes and auto-saves.
+
+ - Yes: encrypt all topics pending encryption, even if it's the one
+        currently being edited.  (In that case, the currently edited topic
+        will be automatically decrypted before any user interaction, so they
+        can continue editing but the copy on the file system will be
+        encrypted.)
+        Auto-saves will use the \"All except current topic\" mode if this
+        one is selected, to avoid practical difficulties - see below.
+ - All except current topic: skip the topic currently being edited, even if
+       it's pending encryption.  This may expose the current topic on the
+       file sytem, but avoids the nuisance of prompts for the encryption
+       passphrase in the middle of editing for, eg, autosaves.
+       This mode is used for auto-saves for both this option and \"Yes\".
+ - No: leave it to the user to encrypt any unencrypted topics.
+
+For practical reasons, auto-saves always use the 'except-current policy
+when auto-encryption is enabled.  (Otherwise, spurious passphrase prompts
+and unavoidable timing collisions are too disruptive.)  If security for a
+file requires that even the current topic is never auto-saved in the clear,
+disable auto-saves for that file."
+
+  :type '(choice (const :tag "Yes" t)
+                 (const :tag "All except current topic" except-current)
+                 (const :tag "No" nil))
+  :version "22.0"
+  :group 'allout-encryption)
+(make-variable-buffer-local 'allout-encrypt-unencrypted-on-saves)
+
+;;;_ + Developer
+;;;_  = allout-developer group
+(defgroup allout-developer nil
+  "Settings for topic encryption features of allout outliner."
   :group 'allout)
-(make-variable-buffer-local 'allout-isearch-dynamic-expose)
+;;;_  = allout-run-unit-tests-on-load
+(defcustom allout-run-unit-tests-on-load nil
+  "*When non-nil, unit tests will be run at end of loading the allout module.
 
-;;;_  = allout-use-hanging-indents
-(defcustom allout-use-hanging-indents t
-  "*If non-nil, topic body text auto-indent defaults to indent of the header.
-Ie, it is indented to be just past the header prefix.  This is
-relevant mostly for use with indented-text-mode, or other situations
-where auto-fill occurs.
-
-\[This feature no longer depends in any way on the `filladapt.el'
-lisp-archive package.\]"
-  :type 'boolean
-  :group 'allout)
-(make-variable-buffer-local 'allout-use-hanging-indents)
+Generally, allout code developers are the only ones who'll want to set this.
 
-;;;_  = allout-reindent-bodies
-(defcustom allout-reindent-bodies (if allout-use-hanging-indents
-                                   'text)
-  "*Non-nil enables auto-adjust of topic body hanging indent with depth shifts.
+\(If set, this makes it an even better practice to exercise changes by
+doing byte-compilation with a repeat count, so the file is loaded after
+compilation.)
 
-When active, topic body lines that are indented even with or beyond
-their topic header are reindented to correspond with depth shifts of
-the header.
+See `allout-run-unit-tests' to see what's run."
+  :type 'boolean
+  :group 'allout-developer)
 
-A value of t enables reindent in non-programming-code buffers, ie
-those that do not have the variable `comment-start' set.  A value of
-`force' enables reindent whether or not `comment-start' is set."
-  :type '(choice (const nil) (const t) (const text) (const force))
-  :group 'allout)
+;;;_ + Miscellaneous customization
 
-(make-variable-buffer-local 'allout-reindent-bodies)
+;;;_  = allout-enable-file-variable-adjustment
+(defcustom allout-enable-file-variable-adjustment t
+  "*If non-nil, some allout outline actions edit Emacs local file var text.
 
-;;;_  = allout-inhibit-protection
-(defcustom allout-inhibit-protection nil
-  "*Non-nil disables warnings and confirmation-checks for concealed-text edits.
+This can range from changes to existing entries, addition of new ones,
+and creation of a new local variables section when necessary.
 
-Outline mode uses Emacs change-triggered functions to detect unruly
-changes to concealed regions.  Set this var non-nil to disable the
-protection, potentially increasing text-entry responsiveness a bit.
+Emacs file variables adjustments are also inhibited if `enable-local-variables'
+is nil.
 
-This var takes effect at `allout-mode' activation, so you may have to
-deactivate and then reactivate the mode if you want to toggle the
-behavior."
+Operations potentially causing edits include allout encryption routines.
+For details, see `allout-toggle-current-subtree-encryption's docstring."
   :type 'boolean
   :group 'allout)
+(make-variable-buffer-local 'allout-enable-file-variable-adjustment)
 
 ;;;_* CODE - no user customizations below.
 
 ;;;_ #1 Internal Outline Formatting and Configuration
 ;;;_  : Version
 ;;;_   = allout-version
-(defvar allout-version
-  (let ((rcs-rev "$Revision$"))
-    (condition-case err
-       (save-match-data
-         (string-match "Revision: \\([0-9]+\\.[0-9]+\\)" rcs-rev)
-         (substring rcs-rev (match-beginning 1) (match-end 1)))
-      ('error rcs-rev)))
-  "Revision number of currently loaded outline package.  \(allout.el)")
+(defvar allout-version "2.2.1"
+  "Version of currently loaded outline package.  (allout.el)")
 ;;;_   > allout-version
 (defun allout-version (&optional here)
   "Return string describing the loaded outline version."
@@ -524,6 +805,42 @@ behavior."
     (if here (insert msg))
     (message "%s" msg)
     msg))
+;;;_  : Mode activation (defined here because it's referenced early)
+;;;_   = allout-mode
+(defvar allout-mode nil "Allout outline mode minor-mode flag.")
+(make-variable-buffer-local 'allout-mode)
+;;;_   = allout-layout nil
+(defvar allout-layout nil            ; LEAVE GLOBAL VALUE NIL - see docstring.
+  "Buffer-specific setting for allout layout.
+
+In buffers where this is non-nil (and if `allout-init' has been run, to
+enable this behavior), `allout-mode' will be automatically activated.  The
+layout dictated by the value will be used to set the initial exposure when
+`allout-mode' is activated.
+
+\*You should not setq-default this variable non-nil unless you want every
+visited file to be treated as an allout file.*
+
+The value would typically be set by a file local variable.  For
+example, the following lines at the bottom of an Emacs Lisp file:
+
+;;;Local variables:
+;;;allout-layout: (0 : -1 -1 0)
+;;;End:
+
+dictate activation of `allout-mode' mode when the file is visited
+\(presuming allout-init was already run), followed by the
+equivalent of `(allout-expose-topic 0 : -1 -1 0)'.  (This is
+the layout used for the allout.el source file.)
+
+`allout-default-layout' describes the specification format.
+`allout-layout' can additionally have the value `t', in which
+case the value of `allout-default-layout' is used.")
+(make-variable-buffer-local 'allout-layout)
+;;;###autoload
+(put 'allout-layout 'safe-local-variable
+     '(lambda (x) (or (numberp x) (listp x) (memq x '(: * + -)))))
+
 ;;;_  : Topic header format
 ;;;_   = allout-regexp
 (defvar allout-regexp ""
@@ -545,18 +862,37 @@ and `allout-distinctive-bullets-string'.")
 (defvar allout-bullets-string-len 0
   "Length of current buffers' `allout-plain-bullets-string'.")
 (make-variable-buffer-local 'allout-bullets-string-len)
+;;;_   = allout-depth-specific-regexp
+(defvar allout-depth-specific-regexp ""
+  "*Regular expression to match a heading line prefix for a particular depth.
+
+This expression is used to search for depth-specific topic
+headers at depth 2 and greater.  Use `allout-depth-one-regexp'
+for to seek topics at depth one.
+
+This var is set according to the user configuration vars by
+`set-allout-regexp'.  It is prepared with format strings for two
+decimal numbers, which should each be one less than the depth of the
+topic prefix to be matched.")
+(make-variable-buffer-local 'allout-depth-specific-regexp)
+;;;_   = allout-depth-one-regexp
+(defvar allout-depth-one-regexp ""
+  "*Regular expression to match a heading line prefix for depth one.
+
+This var is set according to the user configuration vars by
+`set-allout-regexp'.  It is prepared with format strings for two
+decimal numbers, which should each be one less than the depth of the
+topic prefix to be matched.")
+(make-variable-buffer-local 'allout-depth-one-regexp)
 ;;;_   = allout-line-boundary-regexp
 (defvar allout-line-boundary-regexp ()
   "`allout-regexp' with outline style beginning-of-line anchor.
 
-\(Ie, C-j, *or* C-m, for prefixes of hidden topics).  This is properly
-set when `allout-regexp' is produced by `set-allout-regexp', so
-that (match-beginning 2) and (match-end 2) delimit the prefix.")
+This is properly set by `set-allout-regexp'.")
 (make-variable-buffer-local 'allout-line-boundary-regexp)
 ;;;_   = allout-bob-regexp
 (defvar allout-bob-regexp ()
-  "Like `allout-line-boundary-regexp', for headers at beginning of buffer.
-\(match-beginning 2) and \(match-end 2) delimit the prefix.")
+  "Like `allout-line-boundary-regexp', for headers at beginning of buffer.")
 (make-variable-buffer-local 'allout-bob-regexp)
 ;;;_   = allout-header-subtraction
 (defvar allout-header-subtraction (1- (length allout-header-prefix))
@@ -567,7 +903,32 @@ that (match-beginning 2) and (match-end 2) delimit the prefix.")
   "Length of `allout-plain-bullets-string', updated by `set-allout-regexp'.")
 (make-variable-buffer-local 'allout-plain-bullets-string-len)
 
-
+;;;_   = allout-doublecheck-at-and-shallower
+(defconst allout-doublecheck-at-and-shallower 3
+  "Validate apparent topics of this depth and shallower as being non-aberrant.
+
+Verified with `allout-aberrant-container-p'.  The usefulness of
+this check is limited to shallow depths, because the
+determination of aberrance is according to the mistaken item
+being followed by a legitimate item of excessively greater depth.
+
+The classic example of a mistaken item, for a standard allout
+outline configuration, is a body line that begins with an '...'
+ellipsis.  This happens to contain a legitimate depth-2 header
+prefix, constituted by two '..' dots at the beginning of the
+line.  The only thing that can distinguish it *in principle* from
+a legitimate one is if the following real header is at a depth
+that is discontinuous from the depth of 2 implied by the
+ellipsis, ie depth 4 or more.  As the depth being tested gets
+greater, the likelihood of this kind of disqualification is
+lower, and the usefulness of this test is lower.
+
+Extending the depth of the doublecheck increases the amount it is
+applied, increasing the cost of the test - on casual estimation,
+for outlines with many deep topics, geometrically (O(n)?).
+Taken together with decreasing likelihood that the test will be
+useful at greater depths, more modest doublecheck limits are more
+suitably economical.")
 ;;;_   X allout-reset-header-lead (header-lead)
 (defun allout-reset-header-lead (header-lead)
   "*Reset the leading string used to identify topic headers."
@@ -589,9 +950,9 @@ language comments.  Returns the leading string."
   (setq allout-reindent-bodies nil)
   (allout-reset-header-lead header-lead)
   header-lead)
-;;;_   > allout-infer-header-lead ()
-(defun allout-infer-header-lead ()
-  "Determine appropriate `allout-header-prefix'.
+;;;_   > allout-infer-header-lead-and-primary-bullet ()
+(defun allout-infer-header-lead-and-primary-bullet ()
+  "Determine appropriate `allout-header-prefix' and `allout-primary-bullet'.
 
 Works according to settings of:
 
@@ -600,7 +961,7 @@ Works according to settings of:
        `allout-use-mode-specific-leader'
 and    `allout-mode-leaders'.
 
-Apply this via \(re)activation of `allout-mode', rather than
+Apply this via (re)activation of `allout-mode', rather than
 invoking it directly."
   (let* ((use-leader (and (boundp 'allout-use-mode-specific-leader)
                          (if (or (stringp allout-use-mode-specific-leader)
@@ -635,15 +996,19 @@ invoking it directly."
                        "_")))))))
     (if (not leader)
        nil
-      (if (string= leader allout-header-prefix)
-         nil                           ; no change, nothing to do.
-       (setq allout-header-prefix leader)
-       allout-header-prefix))))
+      (setq allout-header-prefix leader)
+      (if (not allout-old-style-prefixes)
+          ;; setting allout-primary-bullet makes the top level topics use -
+          ;; actually, be - the special prefix:
+          (setq allout-primary-bullet leader))
+      allout-header-prefix)))
+(defalias 'allout-infer-header-lead
+  'allout-infer-header-lead-and-primary-bullet)
 ;;;_   > allout-infer-body-reindent ()
 (defun allout-infer-body-reindent ()
   "Determine proper setting for `allout-reindent-bodies'.
 
-Depends on default setting of `allout-reindent-bodies' \(which see)
+Depends on default setting of `allout-reindent-bodies' (which see)
 and presence of setting for `comment-start', to tell whether the
 file is programming code."
   (if (and allout-reindent-bodies
@@ -655,7 +1020,9 @@ file is programming code."
   "Generate proper topic-header regexp form for outline functions.
 
 Works with respect to `allout-plain-bullets-string' and
-`allout-distinctive-bullets-string'."
+`allout-distinctive-bullets-string'.
+
+Also refresh various data structures that hinge on the regexp."
 
   (interactive)
   ;; Derive allout-bullets-string from user configured components:
@@ -666,11 +1033,9 @@ Works with respect to `allout-plain-bullets-string' and
         cur-string
         cur-len
         cur-char
-        cur-char-string
-        index
-        new-string)
+        index)
     (while strings
-      (setq new-string "") (setq index 0)
+      (setq index 0)
       (setq cur-len (length (setq cur-string (symbol-value (car strings)))))
       (while (< index cur-len)
         (setq cur-char (aref cur-string index))
@@ -692,19 +1057,84 @@ Works with respect to `allout-plain-bullets-string' and
   ;; Derive next for repeated use in allout-pending-bullet:
   (setq allout-plain-bullets-string-len (length allout-plain-bullets-string))
   (setq allout-header-subtraction (1- (length allout-header-prefix)))
-  ;; Produce the new allout-regexp:
-  (setq allout-regexp (concat "\\(\\"
-                               allout-header-prefix
-                               "[ \t]*["
-                               allout-bullets-string
-                               "]\\)\\|\\"
-                               allout-primary-bullet
-                               "+\\|\^l"))
-  (setq allout-line-boundary-regexp
-        (concat "\\([\n\r]\\)\\(" allout-regexp "\\)"))
-  (setq allout-bob-regexp
-        (concat "\\(\\`\\)\\(" allout-regexp "\\)"))
-  )
+
+  (let (new-part old-part)
+    (setq new-part (concat "\\("
+                           (regexp-quote allout-header-prefix)
+                           "[ \t]*"
+                           ;; already regexp-quoted in a custom way:
+                           "[" allout-bullets-string "]"
+                           "\\)")
+          old-part (concat "\\("
+                           (regexp-quote allout-primary-bullet)
+                           "\\|"
+                           (regexp-quote allout-header-prefix)
+                           "\\)"
+                           "+"
+                           " ?[^" allout-primary-bullet "]")
+          allout-regexp (concat new-part
+                                "\\|"
+                                old-part
+                                "\\|\^l")
+
+          allout-line-boundary-regexp (concat "\n" new-part
+                                              "\\|"
+                                              "\n" old-part)
+
+          allout-bob-regexp (concat "\\`" new-part
+                                    "\\|"
+                                    "\\`" old-part))
+
+    (setq allout-depth-specific-regexp
+          (concat "\\(^\\|\\`\\)"
+                  "\\("
+
+                  ;; new-style spacers-then-bullet string:
+                  "\\("
+                  (allout-format-quote (regexp-quote allout-header-prefix))
+                  " \\{%s\\}"
+                  "[" (allout-format-quote allout-bullets-string) "]"
+                  "\\)"
+
+                  ;; old-style all-bullets string, if primary not multi-char:
+                  (if (< 0 allout-header-subtraction)
+                      ""
+                    (concat "\\|\\("
+                            (allout-format-quote
+                             (regexp-quote allout-primary-bullet))
+                            (allout-format-quote
+                             (regexp-quote allout-primary-bullet))
+                            (allout-format-quote
+                             (regexp-quote allout-primary-bullet))
+                            "\\{%s\\}"
+                            ;; disqualify greater depths:
+                            "[^"
+                            (allout-format-quote allout-primary-bullet)
+                            "]\\)"
+                            ))
+                  "\\)"
+                  ))
+    (setq allout-depth-one-regexp
+          (concat "\\(^\\|\\`\\)"
+                  "\\("
+
+                  "\\("
+                  (regexp-quote allout-header-prefix)
+                  ;; disqualify any bullet char following any amount of
+                  ;; intervening whitespace:
+                  " *"
+                  (concat "[^ " allout-bullets-string "]")
+                  "\\)"
+                  (if (< 0 allout-header-subtraction)
+                      ;; Need not support anything like the old
+                      ;; bullet style if the prefix is multi-char.
+                      ""
+                    (concat "\\|"
+                            (regexp-quote allout-primary-bullet)
+                            ;; disqualify deeper primary-bullet sequences:
+                            "[^" allout-primary-bullet "]"))
+                  "\\)"
+                  ))))
 ;;;_  : Key bindings
 ;;;_   = allout-mode-map
 (defvar allout-mode-map nil "Keybindings for (allout) outline minor mode.")
@@ -716,19 +1146,18 @@ Built on top of optional BASE-MAP, or empty sparse map if none specified.
 See doc string for allout-keybindings-list for format of binding list."
   (let ((map (or base-map (make-sparse-keymap)))
        (pref (list allout-command-prefix)))
-    (mapcar (function
+    (mapc (function
             (lambda (cell)
               (let ((add-pref (null (cdr (cdr cell))))
                     (key-suff (list (car cell))))
                 (apply 'define-key
                        (list map
-                             (apply 'concat (if add-pref
+                             (apply 'vconcat (if add-pref
                                                 (append pref key-suff)
                                               key-suff))
                              (car (cdr cell)))))))
            keymap-list)
     map))
-
 ;;;_  : Menu bar
 (defvar allout-mode-exposure-menu)
 (defvar allout-mode-editing-menu)
@@ -759,7 +1188,11 @@ See doc string for allout-keybindings-list for format of binding list."
                      ["Shift Topic Out" allout-shift-out t]
                      ["Rebullet Topic" allout-rebullet-topic t]
                      ["Rebullet Heading" allout-rebullet-current-heading t]
-                     ["Number Siblings" allout-number-siblings t]))
+                     ["Number Siblings" allout-number-siblings t]
+                     "----"
+                      ["Toggle Topic Encryption"
+                       allout-toggle-current-subtree-encryption
+                       (> (allout-current-depth) 1)]))
   (easy-menu-define allout-mode-navigation-menu
                    allout-mode-map
                    "Allout outline navigation menu."
@@ -775,7 +1208,7 @@ See doc string for allout-keybindings-list for format of binding list."
                      "----"
                      ["Beginning of Entry"
                       allout-beginning-of-current-entry t]
-                     ["End of Entry" allout-end-of-current-entry t]
+                     ["End of Entry" allout-end-of-entry t]
                      ["End of Subtree" allout-end-of-current-subtree t]))
   (easy-menu-define allout-mode-misc-menu
                    allout-mode-map
@@ -791,147 +1224,350 @@ See doc string for allout-keybindings-list for format of binding list."
                      "----"
                      ["Set Header Lead" allout-reset-header-lead t]
                      ["Set New Exposure" allout-expose-topic t])))
-;;;_  : Mode-Specific Variable Maintenance Utilities
+;;;_  : Allout Modal-Variables Utilities
 ;;;_   = allout-mode-prior-settings
 (defvar allout-mode-prior-settings nil
-  "Internal `allout-mode' use; settings to be resumed on mode deactivation.")
-(make-variable-buffer-local 'allout-mode-prior-settings)
-;;;_   > allout-resumptions (name &optional value)
-(defun allout-resumptions (name &optional value)
-
-  "Registers or resumes settings over `allout-mode' activation/deactivation.
-
-First arg is NAME of variable affected.  Optional second arg is list
-containing allout-mode-specific VALUE to be imposed on named
-variable, and to be registered.  (It's a list so you can specify
-registrations of null values.)  If no value is specified, the
-registered value is returned (encapsulated in the list, so the caller
-can distinguish nil vs no value), and the registration is popped
-from the list."
-
-  (let ((on-list (assq name allout-mode-prior-settings))
-        prior-capsule                   ; By `capsule' i mean a list
-                                        ; containing a value, so we can
-                                        ; distinguish nil from no value.
-        )
-
-    (if value
-
-        ;; Registering:
-        (progn
-          (if on-list
-              nil      ; Already preserved prior value - don't mess with it.
-            ;; Register the old value, or nil if previously unbound:
-            (setq allout-mode-prior-settings
-                  (cons (list name
-                              (if (boundp name) (list (symbol-value name))))
-                        allout-mode-prior-settings)))
-                                        ; And impose the new value, locally:
-         (progn (make-local-variable name)
-                (set name (car value))))
-
-      ;; Relinquishing:
-      (if (not on-list)
-
-          ;; Oops, not registered - leave it be:
-          nil
+  "Internal `allout-mode' use; settings to be resumed on mode deactivation.
 
-        ;; Some registration:
-                                        ; reestablish it:
-        (setq prior-capsule (car (cdr on-list)))
-        (if prior-capsule
-            (set name (car prior-capsule)) ; Some prior value - reestablish it.
-          (makunbound name))           ; Previously unbound - demolish var.
-                                        ; Remove registration:
-        (let (rebuild)
-          (while allout-mode-prior-settings
-            (if (not (eq (car allout-mode-prior-settings)
-                         on-list))
-                (setq rebuild
-                      (cons (car allout-mode-prior-settings)
-                            rebuild)))
-            (setq allout-mode-prior-settings
-                  (cdr allout-mode-prior-settings)))
-          (setq allout-mode-prior-settings rebuild)))))
-  )
+See `allout-add-resumptions' and `allout-do-resumptions'.")
+(make-variable-buffer-local 'allout-mode-prior-settings)
+;;;_   > allout-add-resumptions (&rest pairs)
+(defun allout-add-resumptions (&rest pairs)
+  "Set name/value PAIRS.
+
+Old settings are preserved for later resumption using `allout-do-resumptions'.
+
+The new values are set as a buffer local.  On resumption, the prior buffer
+scope of the variable is restored along with its value.  If it was a void
+buffer-local value, then it is left as nil on resumption.
+
+The pairs are lists whose car is the name of the variable and car of the
+cdr is the new value: '(some-var some-value)'.  The pairs can actually be
+triples, where the third element qualifies the disposition of the setting,
+as described further below.
+
+If the optional third element is the symbol 'extend, then the new value
+created by `cons'ing the second element of the pair onto the front of the
+existing value.
+
+If the optional third element is the symbol 'append, then the new value is
+extended from the existing one by `append'ing a list containing the second
+element of the pair onto the end of the existing value.
+
+Extension, and resumptions in general, should not be used for hook
+functions - use the 'local mode of `add-hook' for that, instead.
+
+The settings are stored on `allout-mode-prior-settings'."
+  (while pairs
+    (let* ((pair (pop pairs))
+           (name (car pair))
+           (value (cadr pair))
+           (qualifier (if (> (length pair) 2)
+                          (caddr pair)))
+           prior-value)
+      (if (not (symbolp name))
+          (error "Pair's name, %S, must be a symbol, not %s"
+                 name (type-of name)))
+      (setq prior-value (condition-case nil
+                            (symbol-value name)
+                          (void-variable nil)))
+      (when (not (assoc name allout-mode-prior-settings))
+        ;; Not already added as a resumption, create the prior setting entry.
+        (if (local-variable-p name)
+            ;; is already local variable - preserve the prior value:
+            (push (list name prior-value) allout-mode-prior-settings)
+          ;; wasn't local variable, indicate so for resumption by killing
+          ;; local value, and make it local:
+          (push (list name) allout-mode-prior-settings)
+          (make-local-variable name)))
+      (if qualifier
+          (cond ((eq qualifier 'extend)
+                 (if (not (listp prior-value))
+                     (error "extension of non-list prior value attempted")
+                   (set name (cons value prior-value))))
+                ((eq qualifier 'append)
+                 (if (not (listp prior-value))
+                     (error "appending of non-list prior value attempted")
+                   (set name (append prior-value (list value)))))
+                (t (error "unrecognized setting qualifier `%s' encountered"
+                          qualifier)))
+        (set name value)))))
+;;;_   > allout-do-resumptions ()
+(defun allout-do-resumptions ()
+  "Resume all name/value settings registered by `allout-add-resumptions'.
+
+This is used when concluding allout-mode, to resume selected variables to
+their settings before allout-mode was started."
+
+    (while allout-mode-prior-settings
+      (let* ((pair (pop allout-mode-prior-settings))
+             (name (car pair))
+             (value-cell (cdr pair)))
+        (if (not value-cell)
+            ;; Prior value was global:
+            (kill-local-variable name)
+          ;; Prior value was explicit:
+          (set name (car value-cell))))))
 ;;;_  : Mode-specific incidentals
-;;;_   = allout-during-write-cue nil
-(defvar allout-during-write-cue nil
-  "Used to inhibit outline change-protection during file write.
-
-See also `allout-post-command-business', `allout-write-file-hook',
-`allout-before-change-protect', and `allout-post-command-business'
-functions.")
-;;;_   = allout-pre-was-isearching nil
-(defvar allout-pre-was-isearching nil
-  "Cue for isearch-dynamic-exposure mechanism, implemented in
-allout-pre- and -post-command-hooks.")
-(make-variable-buffer-local 'allout-pre-was-isearching)
-;;;_   = allout-isearch-prior-pos nil
-(defvar allout-isearch-prior-pos nil
-  "Cue for isearch-dynamic-exposure tracking, used by `allout-isearch-expose'.")
-(make-variable-buffer-local 'allout-isearch-prior-pos)
-;;;_   = allout-override-protect nil
-(defvar allout-override-protect nil
-  "Used in `allout-mode' for regulate of concealed-text protection mechanism.
-
-Allout outline mode regulates alteration of concealed text to protect
-against inadvertent, unnoticed changes.  This is for use by specific,
-native outline functions to temporarily override that protection.
-It's automatically reset to nil after every buffer modification.")
-(make-variable-buffer-local 'allout-override-protect)
 ;;;_   > allout-unprotected (expr)
-(defmacro allout-unprotected (expression)
-  "Evaluate EXPRESSION with `allout-override-protect' let-bound to t."
-  `(let ((allout-override-protect t))
-     ,expression))
-;;;_   = allout-undo-aggregation
-(defvar allout-undo-aggregation 30
-  "Amount of successive self-insert actions to bunch together per undo.
-
-This is purely a kludge variable, regulating the compensation for a bug in
-the way that `before-change-functions' and undo interact.")
-(make-variable-buffer-local 'allout-undo-aggregation)
+(defmacro allout-unprotected (expr)
+  "Enable internal outline operations to alter invisible text."
+  `(let ((inhibit-read-only (if (not buffer-read-only) t))
+         (inhibit-field-text-motion t))
+     ,expr))
+;;;_   = allout-mode-hook
+(defvar allout-mode-hook nil
+  "*Hook that's run when allout mode starts.")
+;;;_   = allout-mode-deactivate-hook
+(defvar allout-mode-deactivate-hook nil
+  "*Hook that's run when allout mode ends.")
+;;;_   = allout-exposure-category
+(defvar allout-exposure-category nil
+  "Symbol for use as allout invisible-text overlay category.")
+;;;_   x allout-view-change-hook
+(defvar allout-view-change-hook nil
+  "*(Deprecated) A hook run after allout outline exposure changes.
+
+Switch to using `allout-exposure-change-hook' instead.  Both hooks are
+currently respected, but the other conveys the details of the exposure
+change via explicit parameters, and this one will eventually be disabled in
+a subsequent allout version.")
+;;;_   = allout-exposure-change-hook
+(defvar allout-exposure-change-hook nil
+  "*Hook that's run after allout outline subtree exposure changes.
+
+It is run at the conclusion of `allout-flag-region'.
+
+Functions on the hook must take three arguments:
+
+ - from - integer indicating the point at the start of the change.
+ - to - integer indicating the point of the end of the change.
+ - flag - change mode: nil for exposure, otherwise concealment.
+
+This hook might be invoked multiple times by a single command.
+
+This hook is replacing `allout-view-change-hook', which is being deprecated
+and eventually will not be invoked.")
+;;;_   = allout-structure-added-hook
+(defvar allout-structure-added-hook nil
+  "*Hook that's run after addition of items to the outline.
+
+Functions on the hook should take two arguments:
+
+ - new-start - integer indicating the point at the start of the first new item.
+ - new-end - integer indicating the point of the end of the last new item.
+
+Some edits that introduce new items may missed by this hook -
+specifically edits that native allout routines do not control.
+
+This hook might be invoked multiple times by a single command.")
+;;;_   = allout-structure-deleted-hook
+(defvar allout-structure-deleted-hook nil
+  "*Hook that's run after disciplined deletion of subtrees from the outline.
+
+Functions on the hook must take two arguments:
+
+ - depth - integer indicating the depth of the subtree that was deleted.
+ - removed-from - integer indicating the point where the subtree was removed.
+
+Some edits that remove or invalidate items may missed by this hook -
+specifically edits that native allout routines do not control.
+
+This hook might be invoked multiple times by a single command.")
+;;;_   = allout-structure-shifted-hook
+(defvar allout-structure-shifted-hook nil
+  "*Hook that's run after shifting of items in the outline.
+
+Functions on the hook should take two arguments:
+
+ - depth-change - integer indicating depth increase, negative for decrease
+ - start - integer indicating the start point of the shifted parent item.
+
+Some edits that shift items can be missed by this hook - specifically edits
+that native allout routines do not control.
+
+This hook might be invoked multiple times by a single command.")
+;;;_   = allout-outside-normal-auto-fill-function
+(defvar allout-outside-normal-auto-fill-function nil
+  "Value of normal-auto-fill-function outside of allout mode.
+
+Used by allout-auto-fill to do the mandated normal-auto-fill-function
+wrapped within allout's automatic fill-prefix setting.")
+(make-variable-buffer-local 'allout-outside-normal-auto-fill-function)
 ;;;_   = file-var-bug hack
 (defvar allout-v18/19-file-var-hack nil
   "Horrible hack used to prevent invalid multiple triggering of outline
 mode from prop-line file-var activation.  Used by `allout-mode' function
 to track repeats.")
-;;;_   > allout-write-file-hook ()
-(defun allout-write-file-hook ()
-  "In `allout-mode', run as a `write-contents-functions' activity.
-
-Currently just sets `allout-during-write-cue', so outline change-protection
-knows to keep inactive during file write."
-  (setq allout-during-write-cue t)
-  nil)
-
-;;;_ #2 Mode activation
-;;;_  = allout-mode
-(defvar allout-mode () "Allout outline mode minor-mode flag.")
-(make-variable-buffer-local 'allout-mode)
-;;;_  > allout-mode-p ()
+;;;_   = allout-passphrase-verifier-string
+(defvar allout-passphrase-verifier-string nil
+  "Setting used to test solicited encryption passphrases against the one
+already associated with a file.
+
+It consists of an encrypted random string useful only to verify that a
+passphrase entered by the user is effective for decryption.  The passphrase
+itself is \*not* recorded in the file anywhere, and the encrypted contents
+are random binary characters to avoid exposing greater susceptibility to
+search attacks.
+
+The verifier string is retained as an Emacs file variable, as well as in
+the Emacs buffer state, if file variable adjustments are enabled.  See
+`allout-enable-file-variable-adjustment' for details about that.")
+(make-variable-buffer-local 'allout-passphrase-verifier-string)
+;;;###autoload
+(put 'allout-passphrase-verifier-string 'safe-local-variable 'stringp)
+;;;_   = allout-passphrase-hint-string
+(defvar allout-passphrase-hint-string ""
+  "Variable used to retain reminder string for file's encryption passphrase.
+
+See the description of `allout-passphrase-hint-handling' for details about how
+the reminder is deployed.
+
+The hint is retained as an Emacs file variable, as well as in the Emacs buffer
+state, if file variable adjustments are enabled.  See
+`allout-enable-file-variable-adjustment' for details about that.")
+(make-variable-buffer-local 'allout-passphrase-hint-string)
+(setq-default allout-passphrase-hint-string "")
+;;;###autoload
+(put 'allout-passphrase-hint-string 'safe-local-variable 'stringp)
+;;;_   = allout-after-save-decrypt
+(defvar allout-after-save-decrypt nil
+  "Internal variable, is nil or has the value of two points:
+
+ - the location of a topic to be decrypted after saving is done
+ - where to situate the cursor after the decryption is performed
+
+This is used to decrypt the topic that was currently being edited, if it
+was encrypted automatically as part of a file write or autosave.")
+(make-variable-buffer-local 'allout-after-save-decrypt)
+;;;_   = allout-encryption-plaintext-sanitization-regexps
+(defvar allout-encryption-plaintext-sanitization-regexps nil
+  "List of regexps whose matches are removed from plaintext before encryption.
+
+This is for the sake of removing artifacts, like escapes, that are added on
+and not actually part of the original plaintext.  The removal is done just
+prior to encryption.
+
+Entries must be symbols that are bound to the desired values.
+
+Each value can be a regexp or a list with a regexp followed by a
+substitution string.  If it's just a regexp, all its matches are removed
+before the text is encrypted.  If it's a regexp and a substitution, the
+substition is used against the regexp matches, a la `replace-match'.")
+(make-variable-buffer-local 'allout-encryption-text-removal-regexps)
+;;;_   = allout-encryption-ciphertext-rejection-regexps
+(defvar allout-encryption-ciphertext-rejection-regexps nil
+  "Variable for regexps matching plaintext to remove before encryption.
+
+This is for the sake of redoing encryption in cases where the ciphertext
+incidentally contains strings that would disrupt mode operation -
+for example, a line that happens to look like an allout-mode topic prefix.
+
+Entries must be symbols that are bound to the desired regexp values.
+
+The encryption will be retried up to
+`allout-encryption-ciphertext-rejection-limit' times, after which an error
+is raised.")
+
+(make-variable-buffer-local 'allout-encryption-ciphertext-rejection-regexps)
+;;;_   = allout-encryption-ciphertext-rejection-ceiling
+(defvar allout-encryption-ciphertext-rejection-ceiling 5
+  "Limit on number of times encryption ciphertext is rejected.
+
+See `allout-encryption-ciphertext-rejection-regexps' for rejection reasons.")
+(make-variable-buffer-local 'allout-encryption-ciphertext-rejection-ceiling)
+;;;_   > allout-mode-p ()
+;; Must define this macro above any uses, or byte compilation will lack
+;; proper def, if file isn't loaded - eg, during emacs build!
 (defmacro allout-mode-p ()
   "Return t if `allout-mode' is active in current buffer."
   'allout-mode)
+;;;_   > allout-write-file-hook-handler ()
+(defun allout-write-file-hook-handler ()
+  "Implement `allout-encrypt-unencrypted-on-saves' policy for file writes."
+
+  (if (or (not (allout-mode-p))
+          (not (boundp 'allout-encrypt-unencrypted-on-saves))
+          (not allout-encrypt-unencrypted-on-saves))
+      nil
+    (let ((except-mark (and (equal allout-encrypt-unencrypted-on-saves
+                                   'except-current)
+                            (point-marker))))
+      (if (save-excursion (goto-char (point-min))
+                          (allout-next-topic-pending-encryption except-mark))
+          (progn
+            (message "auto-encrypting pending topics")
+            (sit-for 0)
+            (condition-case failure
+                (setq allout-after-save-decrypt
+                      (allout-encrypt-decrypted except-mark))
+              (error (progn
+                       (message
+                        "allout-write-file-hook-handler suppressing error %s"
+                        failure)
+                       (sit-for 2))))))
+      ))
+    nil)
+;;;_   > allout-auto-save-hook-handler ()
+(defun allout-auto-save-hook-handler ()
+  "Implement `allout-encrypt-unencrypted-on-saves' policy for auto save."
+
+  (if (and (allout-mode-p) allout-encrypt-unencrypted-on-saves)
+      ;; Always implement 'except-current policy when enabled.
+      (let ((allout-encrypt-unencrypted-on-saves 'except-current))
+        (allout-write-file-hook-handler))))
+;;;_   > allout-after-saves-handler ()
+(defun allout-after-saves-handler ()
+  "Decrypt topic encrypted for save, if it's currently being edited.
+
+Ie, if it was pending encryption and contained the point in its body before
+the save.
+
+We use values stored in `allout-after-save-decrypt' to locate the topic
+and the place for the cursor after the decryption is done."
+  (if (not (and (allout-mode-p)
+                (boundp 'allout-after-save-decrypt)
+                allout-after-save-decrypt))
+      t
+    (goto-char (car allout-after-save-decrypt))
+    (let ((was-modified (buffer-modified-p)))
+      (allout-toggle-subtree-encryption)
+      (if (not was-modified)
+          (set-buffer-modified-p nil)))
+    (goto-char (cadr allout-after-save-decrypt))
+    (setq allout-after-save-decrypt nil))
+  )
+;;;_   = allout-inhibit-aberrance-doublecheck nil
+;; In some exceptional moments, disparate topic depths need to be allowed
+;; momentarily, eg when one topic is being yanked into another and they're
+;; about to be reconciled.  let-binding allout-inhibit-aberrance-doublecheck
+;; prevents the aberrance doublecheck to allow, eg, the reconciliation
+;; processing to happen in the presence of such discrepancies.  It should
+;; almost never be needed, however.
+(defvar allout-inhibit-aberrance-doublecheck nil
+  "Internal state, for momentarily inhibits aberrance doublecheck.
+
+This should only be momentarily let-bound non-nil, not set
+non-nil in a lasting way.")
+
+;;;_ #2 Mode activation
 ;;;_  = allout-explicitly-deactivated
 (defvar allout-explicitly-deactivated nil
-  "Non-nil if `allout-mode' was last deliberately deactivated.
+  "If t, `allout-mode's last deactivation was deliberate.
 So `allout-post-command-business' should not reactivate it...")
 (make-variable-buffer-local 'allout-explicitly-deactivated)
 ;;;_  > allout-init (&optional mode)
-;;;###autoload
 (defun allout-init (&optional mode)
   "Prime `allout-mode' to enable/disable auto-activation, wrt `allout-layout'.
 
 MODE is one of the following symbols:
 
- - nil \(or no argument) deactivate auto-activation/layout;
+ - nil (or no argument) deactivate auto-activation/layout;
  - `activate', enable auto-activation only;
  - `ask', enable auto-activation, and enable auto-layout but with
    confirmation for layout operation solicited from user each time;
  - `report', just report and return the current auto-activation state;
- - anything else \(eg, t) for auto-activation and auto-layout, without
+ - anything else (eg, t) for auto-activation and auto-layout, without
    any confirmation check.
 
 Use this function to setup your Emacs session for automatic activation
@@ -939,9 +1575,9 @@ of allout outline mode, contingent to the buffer-specific setting of
 the `allout-layout' variable.  (See `allout-layout' and
 `allout-expose-topic' docstrings for more details on auto layout).
 
-`allout-init' works by setting up (or removing)
-`allout-find-file-hook' in `find-file-hook', and giving
-`allout-auto-activation' a suitable setting.
+`allout-init' works by setting up (or removing) the `allout-mode'
+find-file-hook, and giving `allout-auto-activation' a suitable
+setting.
 
 To prime your Emacs session for full auto-outline operation, include
 the following two lines in your Emacs init file:
@@ -949,32 +1585,39 @@ the following two lines in your Emacs init file:
 \(require 'allout)
 \(allout-init t)"
 
-  (interactive
-   (let ((m (completing-read
-            (concat "Select outline auto setup mode "
-                    "(empty for report, ? for options) ")
-            '(("nil")("full")("activate")("deactivate")
-              ("ask") ("report") (""))
-            nil
-            t)))
-     (if (string= m "") 'report
-       (intern-soft m))))
+  (interactive)
+  (if (interactive-p)
+      (progn
+       (setq mode
+             (completing-read
+              (concat "Select outline auto setup mode "
+                      "(empty for report, ? for options) ")
+              '(("nil")("full")("activate")("deactivate")
+                ("ask") ("report") (""))
+              nil
+              t))
+       (if (string= mode "")
+           (setq mode 'report)
+         (setq mode (intern-soft mode)))))
   (let
       ;; convenience aliases, for consistent ref to respective vars:
       ((hook 'allout-find-file-hook)
+       (find-file-hook-var-name (if (boundp 'find-file-hook)
+                                    'find-file-hook
+                                  'find-file-hooks))
        (curr-mode 'allout-auto-activation))
 
     (cond ((not mode)
-          (setq find-file-hook (delq hook find-file-hook))
+          (set find-file-hook-var-name
+                (delq hook (symbol-value find-file-hook-var-name)))
           (if (interactive-p)
               (message "Allout outline mode auto-activation inhibited.")))
          ((eq mode 'report)
-          (if (memq hook find-file-hook)
-              ;; Just punt and use the reports from each of the modes:
-              (allout-init (symbol-value curr-mode))
-            (allout-init nil)
-            (message "Allout outline mode auto-activation inhibited.")))
-         (t (add-hook 'find-file-hook hook)
+          (if (not (memq hook (symbol-value find-file-hook-var-name)))
+              (allout-init nil)
+            ;; Just punt and use the reports from each of the modes:
+            (allout-init (symbol-value curr-mode))))
+         (t (add-hook find-file-hook-var-name hook)
             (set curr-mode             ; `set', not `setq'!
                  (cond ((eq mode 'activate)
                         (message
@@ -986,12 +1629,11 @@ the following two lines in your Emacs init file:
                        ((eq mode 'ask)
                         (message
                          (concat "Outline mode auto-activation and "
-                                 "-layout \(upon confirmation) enabled."))
+                                 "-layout (upon confirmation) enabled."))
                         'ask)
                        ((message
                          "Outline mode auto-activation and -layout enabled.")
                         'full)))))))
-
 ;;;_  > allout-setup-menubar ()
 (defun allout-setup-menubar ()
   "Populate the current buffer's menubar with `allout-mode' stuff."
@@ -1004,192 +1646,286 @@ the following two lines in your Emacs init file:
       (setq cur (car menus)
            menus (cdr menus))
       (easy-menu-add cur))))
+;;;_  > allout-overlay-preparations
+(defun allout-overlay-preparations ()
+  "Set the properties of the allout invisible-text overlay and others."
+  (setplist 'allout-exposure-category nil)
+  (put 'allout-exposure-category 'invisible 'allout)
+  (put 'allout-exposure-category 'evaporate t)
+  ;; XXX We use isearch-open-invisible *and* isearch-mode-end-hook.  The
+  ;; latter would be sufficient, but it seems that a separate behavior -
+  ;; the _transient_ opening of invisible text during isearch - is keyed to
+  ;; presence of the isearch-open-invisible property - even though this
+  ;; property controls the isearch _arrival_ behavior.  This is the case at
+  ;; least in emacs 21, 22.0, and xemacs 21.4.
+  (put 'allout-exposure-category 'isearch-open-invisible
+       'allout-isearch-end-handler)
+  (if (featurep 'xemacs)
+      (put 'allout-exposure-category 'start-open t)
+    (put 'allout-exposure-category 'insert-in-front-hooks
+         '(allout-overlay-insert-in-front-handler)))
+  (put 'allout-exposure-category 'modification-hooks
+       '(allout-overlay-interior-modification-handler)))
 ;;;_  > allout-mode (&optional toggle)
 ;;;_   : Defun:
+;;;###autoload
 (defun allout-mode (&optional toggle)
 ;;;_    . Doc string:
   "Toggle minor mode for controlling exposure and editing of text outlines.
+\\<allout-mode-map>
 
-Optional arg forces mode to re-initialize iff arg is positive num or
-symbol.  Allout outline mode always runs as a minor mode.
+Optional prefix argument TOGGLE forces the mode to re-initialize
+if it is positive, otherwise it turns the mode off.  Allout
+outline mode always runs as a minor mode.
 
 Allout outline mode provides extensive outline oriented formatting and
 manipulation.  It enables structural editing of outlines, as well as
 navigation and exposure.  It also is specifically aimed at
-accommodating syntax-sensitive text like programming languages.  \(For
+accommodating syntax-sensitive text like programming languages.  (For
 an example, see the allout code itself, which is organized as an allout
 outline.)
 
-In addition to outline navigation and exposure, allout includes:
+In addition to typical outline navigation and exposure, allout includes:
 
- - topic-oriented repositioning, cut, and paste
- - integral outline exposure-layout
+ - topic-oriented authoring, including keystroke-based topic creation,
+   repositioning, promotion/demotion, cut, and paste
  - incremental search with dynamic exposure and reconcealment of hidden text
- - automatic topic-number maintenance
- - \"Hot-spot\" operation, for single-keystroke maneuvering and
-    exposure control.  \(See the allout-mode docstring.)
+ - adjustable format, so programming code can be developed in outline-structure
+ - easy topic encryption and decryption
+ - \"Hot-spot\" operation, for single-keystroke maneuvering and exposure control
+ - integral outline layout, for automatic initial exposure when visiting a file
+ - independent extensibility, using comprehensive exposure and authoring hooks
 
 and many other features.
 
-Below is a description of the bindings, and then explanation of
+Below is a description of the key bindings, and then explanation of
 special `allout-mode' features and terminology.  See also the outline
 menubar additions for quick reference to many of the features, and see
 the docstring of the function `allout-init' for instructions on
-priming your Emacs session for automatic activation of `allout-mode'.
-
-
-The bindings are dictated by the `allout-keybindings-list' and
-`allout-command-prefix' variables.
-
-       Navigation:                                Exposure Control:
-       ----------                                 ----------------
-C-c C-n allout-next-visible-heading     | C-c C-h allout-hide-current-subtree
-C-c C-p allout-previous-visible-heading | C-c C-i allout-show-children
-C-c C-u allout-up-current-level         | C-c C-s allout-show-current-subtree
-C-c C-f allout-forward-current-level    | C-c C-o allout-show-current-entry
-C-c C-b allout-backward-current-level   | ^U C-c C-s allout-show-all
-C-c C-e allout-end-of-current-entry     |         allout-hide-current-leaves
-C-c C-a allout-beginning-of-current-entry, alternately, goes to hot-spot
-
-       Topic Header Production:
-       -----------------------
-C-c<SP>        allout-open-sibtopic    Create a new sibling after current topic.
-C-c .  allout-open-subtopic    ... an offspring of current topic.
-C-c ,  allout-open-supertopic  ... a sibling of the current topic's parent.
-
-       Topic Level and Prefix Adjustment:
-       ---------------------------------
-C-c >  allout-shift-in Shift current topic and all offspring deeper.
-C-c <  allout-shift-out        ... less deep.
-C-c<CR>        allout-rebullet-topic   Reconcile bullets of topic and its offspring
-                               - distinctive bullets are not changed, others
-                                 alternated according to nesting depth.
-C-c *  allout-rebullet-current-heading Prompt for alternate bullet for
-                                        current topic.
-C-c #  allout-number-siblings  Number bullets of topic and siblings - the
-                               offspring are not affected.  With repeat
-                               count, revoke numbering.
-
-       Topic-oriented Killing and Yanking:
-       ----------------------------------
-C-c C-k        allout-kill-topic       Kill current topic, including offspring.
-C-k    allout-kill-line        Like kill-line, but reconciles numbering, etc.
-C-y    allout-yank             Yank, adjusting depth of yanked topic to
-                               depth of heading if yanking into bare topic
-                               heading (ie, prefix sans text).
-M-y    allout-yank-pop Is to allout-yank as yank-pop is to yank
-
-       Misc commands:
-       -------------
-M-x outlineify-sticky          Activate outline mode for current buffer,
-                               and establish a default file-var setting
-                               for `allout-layout'.
-C-c C-SPC allout-mark-topic
-C-c = c        allout-copy-exposed-to-buffer
-                               Duplicate outline, sans concealed text, to
-                               buffer with name derived from derived from
-                               that of current buffer - \"*XXX exposed*\".
-C-c = p        allout-flatten-exposed-to-buffer
-                               Like above 'copy-exposed', but convert topic
-                               prefixes to section.subsection... numeric
-                               format.
-ESC ESC (allout-init t)        Setup Emacs session for outline mode
-                               auto-activation.
-
-                HOT-SPOT Operation
+priming your emacs session for automatic activation of `allout-mode'.
+
+The bindings are dictated by the customizable `allout-keybindings-list'
+variable.  We recommend customizing `allout-command-prefix' to use just
+`\\C-c' as the command prefix, if the allout bindings don't conflict with
+any personal bindings you have on \\C-c.  In any case, outline structure
+navigation and authoring is simplified by positioning the cursor on an
+item's bullet character, the \"hot-spot\" - then you can invoke allout
+commands with just the un-prefixed, un-control-shifted command letters.
+This is described further in the HOT-SPOT Operation section.
+
+        Exposure Control:
+        ----------------
+\\[allout-hide-current-subtree]   `allout-hide-current-subtree'
+\\[allout-show-children] `allout-show-children'
+\\[allout-show-current-subtree] `allout-show-current-subtree'
+\\[allout-show-current-entry] `allout-show-current-entry'
+\\[allout-show-all]   `allout-show-all'
+
+        Navigation:
+        ----------
+\\[allout-next-visible-heading] `allout-next-visible-heading'
+\\[allout-previous-visible-heading] `allout-previous-visible-heading'
+\\[allout-up-current-level] `allout-up-current-level'
+\\[allout-forward-current-level] `allout-forward-current-level'
+\\[allout-backward-current-level] `allout-backward-current-level'
+\\[allout-end-of-entry] `allout-end-of-entry'
+\\[allout-beginning-of-current-entry] `allout-beginning-of-current-entry' (alternately, goes to hot-spot)
+\\[allout-beginning-of-line]  `allout-beginning-of-line' - like regular beginning-of-line, but
+     if immediately repeated cycles to the beginning of the current item
+     and then to the hot-spot (if `allout-beginning-of-line-cycles' is set).
+
+
+        Topic Header Production:
+        -----------------------
+\\[allout-open-sibtopic] `allout-open-sibtopic' Create a new sibling after current topic.
+\\[allout-open-subtopic]   `allout-open-subtopic' ... an offspring of current topic.
+\\[allout-open-supertopic] `allout-open-supertopic' ... a sibling of the current topic's parent.
+
+        Topic Level and Prefix Adjustment:
+        ---------------------------------
+\\[allout-shift-in] `allout-shift-in'   Shift current topic and all offspring deeper
+\\[allout-shift-out] `allout-shift-out' ... less deep
+\\[allout-rebullet-current-heading] `allout-rebullet-current-heading' Prompt for alternate bullet for
+            current topic
+\\[allout-rebullet-topic] `allout-rebullet-topic'   Reconcile bullets of topic and
+            its' offspring - distinctive bullets are not changed, others
+            are alternated according to nesting depth.
+\\[allout-number-siblings] `allout-number-siblings'  Number bullets of topic and siblings -
+           the offspring are not affected.
+           With repeat count, revoke numbering.
+
+        Topic-oriented Killing and Yanking:
+        ----------------------------------
+\\[allout-kill-topic] `allout-kill-topic'   Kill current topic, including offspring.
+\\[allout-copy-topic-as-kill] `allout-copy-topic-as-kill' Copy current topic, including offspring.
+\\[allout-kill-line]     `allout-kill-line'    kill-line, attending to outline structure.
+\\[allout-copy-line-as-kill]     `allout-copy-line-as-kill' Copy line but don't delete it.
+\\[allout-yank] `allout-yank'        Yank, adjusting depth of yanked topic to
+                             depth of heading if yanking into bare topic
+                             heading (ie, prefix sans text).
+\\[allout-yank-pop]     `allout-yank-pop'       Is to allout-yank as yank-pop is to yank
+
+        Topic-oriented Encryption:
+        -------------------------
+\\[allout-toggle-current-subtree-encryption] `allout-toggle-current-subtree-encryption'
+          Encrypt/Decrypt topic content
+
+        Misc commands:
+        -------------
+M-x outlineify-sticky       Activate outline mode for current buffer,
+                            and establish a default file-var setting
+                            for `allout-layout'.
+\\[allout-mark-topic]       `allout-mark-topic'
+\\[allout-copy-exposed-to-buffer] `allout-copy-exposed-to-buffer'
+                            Duplicate outline, sans concealed text, to
+                            buffer with name derived from derived from that
+                            of current buffer - \"*BUFFERNAME exposed*\".
+\\[allout-flatten-exposed-to-buffer] `allout-flatten-exposed-to-buffer'
+                            Like above 'copy-exposed', but convert topic
+                            prefixes to section.subsection... numeric
+                            format.
+\\[eval-expression] (allout-init t) Setup Emacs session for outline mode
+                            auto-activation.
+
+                  Topic Encryption
+
+Outline mode supports gpg encryption of topics, with support for
+symmetric and key-pair modes, passphrase timeout, passphrase
+consistency checking, user-provided hinting for symmetric key
+mode, and auto-encryption of topics pending encryption on save.
+
+Topics pending encryption are, by default, automatically
+encrypted during file saves.  If the contents of the topic
+containing the cursor was encrypted for a save, it is
+automatically decrypted for continued editing.
+
+The aim of these measures is reliable topic privacy while
+preventing accidents like neglected encryption before saves,
+forgetting which passphrase was used, and other practical
+pitfalls.
+
+See `allout-toggle-current-subtree-encryption' function docstring
+and `allout-encrypt-unencrypted-on-saves' customization variable
+for details.
+
+                 HOT-SPOT Operation
 
 Hot-spot operation provides a means for easy, single-keystroke outline
 navigation and exposure control.
 
-\\<allout-mode-map>
 When the text cursor is positioned directly on the bullet character of
 a topic, regular characters (a to z) invoke the commands of the
 corresponding allout-mode keymap control chars.  For example, \"f\"
-would invoke the command typically bound to \"C-c C-f\"
+would invoke the command typically bound to \"C-c<space>C-f\"
 \(\\[allout-forward-current-level] `allout-forward-current-level').
 
-Thus, by positioning the cursor on a topic bullet, you can execute
-the outline navigation and manipulation commands with a single
-keystroke.  Non-literal chars never get this special translation, so
-you can use them to get away from the hot-spot, and back to normal
-operation.
+Thus, by positioning the cursor on a topic bullet, you can
+execute the outline navigation and manipulation commands with a
+single keystroke.  Regular navigation keys (eg, \\[forward-char], \\[next-line]) don't get
+this special translation, so you can use them to get out of the
+hot-spot and back to normal editing operation.
+
+In allout-mode, the normal beginning-of-line command (\\[allout-beginning-of-line]]) is
+replaced with one that makes it easy to get to the hot-spot.  If you
+repeat it immediately it cycles (if `allout-beginning-of-line-cycles'
+is set) to the beginning of the item and then, if you hit it again
+immediately, to the hot-spot.  Similarly, `allout-beginning-of-current-entry'
+\(\\[allout-beginning-of-current-entry]) moves to the hot-spot when the cursor is already located
+at the beginning of the current entry.
+
+                             Extending Allout
 
-Note that the command `allout-beginning-of-current-entry' \(\\[allout-beginning-of-current-entry]\)
-will move to the hot-spot when the cursor is already located at the
-beginning of the current entry, so you can simply hit \\[allout-beginning-of-current-entry]
-twice in a row to get to the hot-spot.
+Allout exposure and authoring activites all have associated
+hooks, by which independent code can cooperate with allout
+without changes to the allout core.  Here are key ones:
 
-                           Terminology
+`allout-mode-hook'
+`allout-mode-deactivate-hook'
+`allout-exposure-change-hook'
+`allout-structure-added-hook'
+`allout-structure-deleted-hook'
+`allout-structure-shifted-hook'
+
+                            Terminology
 
 Topic hierarchy constituents - TOPICS and SUBTOPICS:
 
-TOPIC: A basic, coherent component of an Emacs outline.  It can
-       contain other topics, and it can be subsumed by other topics,
-CURRENT topic:
-       The visible topic most immediately containing the cursor.
-DEPTH: The degree of nesting of a topic; it increases with
-       containment.  Also called the:
-LEVEL: The same as DEPTH.
+ITEM:   A unitary outline element, including the HEADER and ENTRY text.
+TOPIC:  An ITEM and any ITEMs contained within it, ie having greater DEPTH
+        and with no intervening items of lower DEPTH than the container.
+CURRENT ITEM:
+        The visible ITEM most immediately containing the cursor.
+DEPTH:  The degree of nesting of an ITEM; it increases with containment.
+        The DEPTH is determined by the HEADER PREFIX.  The DEPTH is also
+        called the:
+LEVEL:  The same as DEPTH.
 
 ANCESTORS:
-       The topics that contain a topic.
-PARENT:        A topic's immediate ancestor.  It has a depth one less than
-       the topic.
+        Those ITEMs whose TOPICs contain an ITEM.
+PARENT: An ITEM's immediate ANCESTOR.  It has a DEPTH one less than that
+        of the ITEM.
 OFFSPRING:
-       The topics contained by a topic;
+        The ITEMs contained within an ITEM's TOPIC.
 SUBTOPIC:
-       An immediate offspring of a topic;
-CHILDREN:
-       The immediate offspring of a topic.
+        An OFFSPRING of its ANCESTOR TOPICs.
+CHILD:
+        An immediate SUBTOPIC of its PARENT.
 SIBLINGS:
-       Topics having the same parent and depth.
+        TOPICs having the same PARENT and DEPTH.
 
 Topic text constituents:
 
-HEADER:        The first line of a topic, include the topic PREFIX and header
-       text.
-PREFIX: The leading text of a topic which distinguishes it from
-       normal text.  It has a strict form, which consists of a
-       prefix-lead string, padding, and a bullet.  The bullet may be
-       followed by a number, indicating the ordinal number of the
-       topic among its siblings, a space, and then the header text.
-
-       The relative length of the PREFIX determines the nesting depth
-       of the topic.
+HEADER: The first line of an ITEM, include the ITEM PREFIX and HEADER
+        text.
+ENTRY:  The text content of an ITEM, before any OFFSPRING, but including
+        the HEADER text and distinct from the ITEM PREFIX.
+BODY:   Same as ENTRY.
+PREFIX: The leading text of an ITEM which distinguishes it from normal
+        ENTRY text.  Allout recognizes the outline structure according
+        to the strict PREFIX format.  It consists of a PREFIX-LEAD string,
+        PREFIX-PADDING, and a BULLET.  The BULLET might be followed by a
+        number, indicating the ordinal number of the topic among its
+        siblings, or an asterisk indicating encryption, plus an optional
+        space.  After that is the ITEM HEADER text, which is not part of
+        the PREFIX.
+
+        The relative length of the PREFIX determines the nesting DEPTH
+        of the ITEM.
 PREFIX-LEAD:
-       The string at the beginning of a topic prefix, normally a `.'.
-       It can be customized by changing the setting of
-       `allout-header-prefix' and then reinitializing `allout-mode'.
-
-       By setting the prefix-lead to the comment-string of a
-       programming language, you can embed outline structuring in
-       program code without interfering with the language processing
-       of that code.  See `allout-use-mode-specific-leader'
-       docstring for more detail.
+        The string at the beginning of a HEADER PREFIX, by default a `.'.
+        It can be customized by changing the setting of
+        `allout-header-prefix' and then reinitializing `allout-mode'.
+
+        When the PREFIX-LEAD is set to the comment-string of a
+        programming language, outline structuring can be embedded in
+        program code without interfering with processing of the text
+        (by emacs or the language processor) as program code.  This
+        setting happens automatically when allout mode is used in
+        programming-mode buffers.  See `allout-use-mode-specific-leader'
+        docstring for more detail.
 PREFIX-PADDING:
-       Spaces or asterisks which separate the prefix-lead and the
-       bullet, according to the depth of the topic.
-BULLET: A character at the end of the topic prefix, it must be one of
-       the characters listed on `allout-plain-bullets-string' or
-        `allout-distinctive-bullets-string'.  (See the documentation
-        for these variables for more details.)  The default choice of
-       bullet when generating varies in a cycle with the depth of the
-       topic.
-ENTRY: The text contained in a topic before any offspring.
-BODY:  Same as ENTRY.
-
-
+        Spaces or asterisks which separate the PREFIX-LEAD and the
+        bullet, determining the ITEM's DEPTH.
+BULLET: A character at the end of the ITEM PREFIX, it must be one of
+        the characters listed on `allout-plain-bullets-string' or
+        `allout-distinctive-bullets-string'.  When creating a TOPIC,
+        plain BULLETs are by default used, according to the DEPTH of the
+        TOPIC.  Choice among the distinctive BULLETs is offered when you
+        provide a universal argugment \(\\[universal-argument]) to the
+        TOPIC creation command, or when explictly rebulleting a TOPIC.  The
+        significance of the various distinctive bullets is purely by
+        convention.  See the documentation for the above bullet strings for
+        more details.
 EXPOSURE:
-       The state of a topic which determines the on-screen visibility
-       of its offspring and contained text.
+        The state of a TOPIC which determines the on-screen visibility
+        of its OFFSPRING and contained ENTRY text.
 CONCEALED:
-       Topics and entry text whose display is inhibited.  Contiguous
-       units of concealed text is represented by `...' ellipses.
-       (Ref the `selective-display' var.)
+        TOPICs and ENTRY text whose EXPOSURE is inhibited.  Concealed
+        text is represented by \"...\" ellipses.
 
-       Concealed topics are effectively collapsed within an ancestor.
-CLOSED:        A topic whose immediate offspring and body-text is concealed.
-OPEN:  A topic that is not closed, though its offspring or body may be."
+        CONCEALED TOPICs are effectively collapsed within an ANCESTOR.
+CLOSED: A TOPIC whose immediate OFFSPRING and body-text is CONCEALED.
+OPEN:  A TOPIC that is not CLOSED, though its OFFSPRING or BODY may be."
 ;;;_    . Code
   (interactive "P")
 
@@ -1200,15 +1936,18 @@ OPEN:   A topic that is not closed, though its offspring or body may be."
                     (or (and (listp toggle)(car toggle))
                         toggle)))
                                       ; Activation specifically demanded?
-        (explicit-activation (or
-                             ;;
-                             (and toggle
+        (explicit-activation (and toggle
                                   (or (symbolp toggle)
-                                      (and (natnump toggle)
-                                           (not (zerop toggle)))))))
+                                      (and (wholenump toggle)
+                                           (not (zerop toggle))))))
         ;; allout-mode already called once during this complex command?
         (same-complex-command (eq allout-v18/19-file-var-hack
                                  (car command-history)))
+         (write-file-hook-var-name (cond ((boundp 'write-file-functions)
+                                          'write-file-functions)
+                                         ((boundp 'write-file-hooks)
+                                          'write-file-hooks)
+                                         (t 'local-write-file-hooks)))
         do-layout
         )
 
@@ -1223,7 +1962,7 @@ OPEN:     A topic that is not closed, though its offspring or body may be."
      ;; off on second invocation, so we detect it as best we can, and
      ;; skip everything.
      ((and same-complex-command                ; Still in same complex command
-                                       ; as last time `allout-mode' invoked.
+                                        ; as last time `allout-mode' invoked.
          active                        ; Already activated.
          (not explicit-activation)     ; Prop-line file-vars don't have args.
          (string-match "^19.1[89]"     ; Bug only known to be in v19.18 and
@@ -1239,95 +1978,88 @@ OPEN:   A topic that is not closed, though its offspring or body may be."
                                       ; specifically requested:
       (setq allout-explicitly-deactivated t)
 
-      (if allout-old-style-prefixes
-         (progn
-          (allout-resumptions 'allout-primary-bullet)
-          (allout-resumptions 'allout-old-style-prefixes)))
-      (allout-resumptions 'selective-display)
-      (if (and (boundp 'before-change-functions) before-change-functions)
-         (allout-resumptions 'before-change-functions))
-      (setq write-contents-functions
-           (delq 'allout-write-file-hook
-                 write-contents-functions))
-      (allout-resumptions 'paragraph-start)
-      (allout-resumptions 'paragraph-separate)
-      (allout-resumptions (if (string-match "^18" emacs-version)
-                             'auto-fill-hook
-                           'auto-fill-function))
-      (allout-resumptions 'allout-former-auto-filler)
-      (setq allout-mode nil))
+      (allout-do-resumptions)
+
+      (remove-from-invisibility-spec '(allout . t))
+      (remove-hook 'pre-command-hook 'allout-pre-command-business t)
+      (remove-hook 'post-command-hook 'allout-post-command-business t)
+      (remove-hook 'before-change-functions 'allout-before-change-handler t)
+      (remove-hook 'isearch-mode-end-hook 'allout-isearch-end-handler t)
+      (remove-hook write-file-hook-var-name 'allout-write-file-hook-handler t)
+      (remove-hook 'auto-save-hook 'allout-auto-save-hook-handler t)
+
+      (remove-overlays (point-min) (point-max)
+                       'category 'allout-exposure-category)
+
+      (setq allout-mode nil)
+      (run-hooks 'allout-mode-deactivate-hook))
 
      ;; Activation:
      ((not active)
       (setq allout-explicitly-deactivated nil)
       (if allout-old-style-prefixes
-         (progn                        ; Inhibit all the fancy formatting:
-          (allout-resumptions 'allout-primary-bullet '("*"))
-          (allout-resumptions 'allout-old-style-prefixes '(()))))
+          ;; Inhibit all the fancy formatting:
+          (allout-add-resumptions '(allout-primary-bullet "*")))
+
+      (allout-overlay-preparations)     ; Doesn't hurt to redo this.
 
-      (allout-infer-header-lead)
+      (allout-infer-header-lead-and-primary-bullet)
       (allout-infer-body-reindent)
 
       (set-allout-regexp)
-
-                                      ; Produce map from current version
-                                      ; of allout-keybindings-list:
-      (if (boundp 'minor-mode-map-alist)
-
-         (progn                        ; V19, and maybe lucid and
-                                      ; epoch, minor-mode key bindings:
-          (setq allout-mode-map
-                (produce-allout-mode-map allout-keybindings-list))
-          (produce-allout-mode-menubar-entries)
-          (fset 'allout-mode-map allout-mode-map)
-                                      ; Include on minor-mode-map-alist,
-                                      ; if not already there:
-          (if (not (member '(allout-mode . allout-mode-map)
-                           minor-mode-map-alist))
-              (setq minor-mode-map-alist
-                    (cons '(allout-mode . allout-mode-map)
-                          minor-mode-map-alist))))
-
-                                      ; and add them:
-       (use-local-map (produce-allout-mode-map allout-keybindings-list
-                                               (current-local-map)))
-       )
-
-                                      ; selective-display is the
-                                      ; Emacs conditional exposure
-                                      ; mechanism:
-      (allout-resumptions 'selective-display '(t))
-      (if allout-inhibit-protection
-         t
-       (allout-resumptions 'before-change-functions
-                           '(allout-before-change-protect)))
-      (add-hook 'pre-command-hook 'allout-pre-command-business)
-      (add-hook 'post-command-hook 'allout-post-command-business)
-                                      ; Temporarily set by any outline
-                                      ; functions that can be trusted to
-                                      ; deal properly with concealed text.
-      (add-hook 'write-contents-functions 'allout-write-file-hook)
-                                      ; Custom auto-fill func, to support
-                                      ; respect for topic headline,
-                                      ; hanging-indents, etc:
-      (let* ((fill-func-var (if (string-match "^18" emacs-version)
-                              'auto-fill-hook
-                            'auto-fill-function))
-           (fill-func (symbol-value fill-func-var)))
-       ;; Register prevailing fill func for use by allout-auto-fill:
-       (allout-resumptions 'allout-former-auto-filler (list fill-func))
-       ;; Register allout-auto-fill to be used if filling is active:
-       (allout-resumptions fill-func-var '(allout-auto-fill)))
-      ;; Paragraphs are broken by topic headlines.
-      (make-local-variable 'paragraph-start)
-      (allout-resumptions 'paragraph-start
-                         (list (concat paragraph-start "\\|^\\("
-                                       allout-regexp "\\)")))
-      (make-local-variable 'paragraph-separate)
-      (allout-resumptions 'paragraph-separate
-                         (list (concat paragraph-separate "\\|^\\("
-                                       allout-regexp "\\)")))
-
+      (allout-add-resumptions
+       '(allout-encryption-ciphertext-rejection-regexps
+         allout-line-boundary-regexp
+         extend)
+       '(allout-encryption-ciphertext-rejection-regexps
+         allout-bob-regexp
+         extend))
+
+      ;; Produce map from current version of allout-keybindings-list:
+      (allout-setup-mode-map)
+      (produce-allout-mode-menubar-entries)
+
+      ;; Include on minor-mode-map-alist, if not already there:
+      (if (not (member '(allout-mode . allout-mode-map)
+                       minor-mode-map-alist))
+          (setq minor-mode-map-alist
+                (cons '(allout-mode . allout-mode-map)
+                      minor-mode-map-alist)))
+
+      (add-to-invisibility-spec '(allout . t))
+
+      (allout-add-resumptions '(line-move-ignore-invisible t))
+      (add-hook 'pre-command-hook 'allout-pre-command-business nil t)
+      (add-hook 'post-command-hook 'allout-post-command-business nil t)
+      (add-hook 'before-change-functions 'allout-before-change-handler
+                nil t)
+      (add-hook 'isearch-mode-end-hook 'allout-isearch-end-handler nil t)
+      (add-hook write-file-hook-var-name 'allout-write-file-hook-handler
+                nil t)
+      (add-hook 'auto-save-hook 'allout-auto-save-hook-handler
+                nil t)
+
+      ;; Stash auto-fill settings and adjust so custom allout auto-fill
+      ;; func will be used if auto-fill is active or activated.  (The
+      ;; custom func respects topic headline, maintains hanging-indents,
+      ;; etc.)
+      (if (and auto-fill-function (not allout-inhibit-auto-fill))
+          ;; allout-auto-fill will use the stashed values and so forth.
+          (allout-add-resumptions '(auto-fill-function allout-auto-fill)))
+      (allout-add-resumptions (list 'allout-former-auto-filler
+                                    auto-fill-function)
+                              ;; Register allout-auto-fill to be used if
+                              ;; filling is active:
+                              (list 'allout-outside-normal-auto-fill-function
+                                    normal-auto-fill-function)
+                              '(normal-auto-fill-function allout-auto-fill)
+                              ;; Paragraphs are broken by topic headlines.
+                              (list 'paragraph-start
+                                    (concat paragraph-start "\\|^\\("
+                                            allout-regexp "\\)"))
+                              (list 'paragraph-separate
+                                    (concat paragraph-separate "\\|^\\("
+                                            allout-regexp "\\)")))
       (or (assq 'allout-mode minor-mode-alist)
          (setq minor-mode-alist
               (cons '(allout-mode " Allout") minor-mode-alist)))
@@ -1337,48 +2069,161 @@ OPEN:  A topic that is not closed, though its offspring or body may be."
       (if allout-layout
          (setq do-layout t))
 
-      (if allout-isearch-dynamic-expose
-         (allout-enwrap-isearch))
-
-      (run-hooks 'allout-mode-hook)
-      (setq allout-mode t))
+      (setq allout-mode t)
+      (run-hooks 'allout-mode-hook))
 
      ;; Reactivation:
      ((setq do-layout t)
       (allout-infer-body-reindent))
-     )                                 ; cond
-
-    (if (and do-layout
-            allout-auto-activation
-            (listp allout-layout)
-            (and (not (eq allout-auto-activation 'activate))
-                 (if (eq allout-auto-activation 'ask)
-                     (if (y-or-n-p (format "Expose %s with layout '%s'? "
-                                           (buffer-name)
-                                           allout-layout))
-                         t
-                       (message "Skipped %s layout." (buffer-name))
-                       nil)
-                   t)))
-       (save-excursion
-         (message "Adjusting '%s' exposure..." (buffer-name))
-         (goto-char 0)
-         (allout-this-or-next-heading)
-         (condition-case err
-             (progn
-               (apply 'allout-expose-topic (list allout-layout))
-               (message "Adjusting '%s' exposure... done." (buffer-name)))
-           ;; Problem applying exposure - notify user, but don't
-           ;; interrupt, eg, file visit:
-           (error (message "%s" (car (cdr err)))
-                  (sit-for 1)))))
+     ) ;; end of activation-mode cases.
+
+    ;; Do auto layout if warranted:
+    (let ((use-layout (if (listp allout-layout)
+                          allout-layout
+                        allout-default-layout)))
+      (if (and do-layout
+               allout-auto-activation
+               use-layout
+               (and (not (eq allout-auto-activation 'activate))
+                    (if (eq allout-auto-activation 'ask)
+                        (if (y-or-n-p (format "Expose %s with layout '%s'? "
+                                              (buffer-name)
+                                              use-layout))
+                            t
+                          (message "Skipped %s layout." (buffer-name))
+                          nil)
+                      t)))
+          (save-excursion
+            (message "Adjusting '%s' exposure..." (buffer-name))
+            (goto-char 0)
+            (allout-this-or-next-heading)
+            (condition-case err
+                (progn
+                  (apply 'allout-expose-topic (list use-layout))
+                  (message "Adjusting '%s' exposure... done." (buffer-name)))
+              ;; Problem applying exposure - notify user, but don't
+              ;; interrupt, eg, file visit:
+              (error (message "%s" (car (cdr err)))
+                     (sit-for 1))))))
     allout-mode
     )                                  ; let*
   )                                    ; defun
+
+(defun allout-setup-mode-map ()
+  "Establish allout-mode bindings."
+  (setq-default allout-mode-map
+                (produce-allout-mode-map allout-keybindings-list))
+  (setq allout-mode-map
+        (produce-allout-mode-map allout-keybindings-list))
+  (substitute-key-definition 'beginning-of-line
+                             'allout-beginning-of-line
+                             allout-mode-map global-map)
+  (substitute-key-definition 'move-beginning-of-line
+                             'allout-beginning-of-line
+                             allout-mode-map global-map)
+  (substitute-key-definition 'end-of-line
+                             'allout-end-of-line
+                             allout-mode-map global-map)
+  (substitute-key-definition 'move-end-of-line
+                             'allout-end-of-line
+                             allout-mode-map global-map)
+  (fset 'allout-mode-map allout-mode-map))
+
+;; ensure that allout-mode-map has some setting even if allout-mode hasn't
+;; been invoked:
+(allout-setup-mode-map)
+
 ;;;_  > allout-minor-mode
-;;; XXX released verion doesn't do this?
 (defalias 'allout-minor-mode 'allout-mode)
 
+;;;_  - Position Assessment
+;;;_   > allout-hidden-p (&optional pos)
+(defsubst allout-hidden-p (&optional pos)
+  "Non-nil if the character after point is invisible."
+  (eq (get-char-property (or pos (point)) 'invisible) 'allout))
+
+;;;_  > allout-overlay-insert-in-front-handler (ol after beg end
+;;;                                                &optional prelen)
+(defun allout-overlay-insert-in-front-handler (ol after beg end
+                                                  &optional prelen)
+  "Shift the overlay so stuff inserted in front of it is excluded."
+  (if after
+      ;; XXX Shouldn't moving the overlay should be unnecessary, if overlay
+      ;;     front-advance on the overlay worked as it should?
+      (move-overlay ol (1+ beg) (overlay-end ol))))
+;;;_  > allout-overlay-interior-modification-handler (ol after beg end
+;;;                                                      &optional prelen)
+(defun allout-overlay-interior-modification-handler (ol after beg end
+                                                        &optional prelen)
+  "Get confirmation before making arbitrary changes to invisible text.
+
+We expose the invisible text and ask for confirmation.  Refusal or
+keyboard-quit abandons the changes, with keyboard-quit additionally
+reclosing the opened text.
+
+No confirmation is necessary when inhibit-read-only is set - eg, allout
+internal functions use this feature cohesively bunch changes."
+
+  (when (and (not inhibit-read-only) (not after))
+    (let ((start (point))
+          (ol-start (overlay-start ol))
+          (ol-end (overlay-end ol))
+          first)
+      (goto-char beg)
+      (while (< (point) end)
+        (when (allout-hidden-p)
+          (allout-show-to-offshoot)
+          (if (allout-hidden-p)
+              (save-excursion (forward-char 1)
+                              (allout-show-to-offshoot)))
+          (when (not first)
+            (setq first (point))))
+        (goto-char (if (featurep 'xemacs)
+                       (next-property-change (1+ (point)) nil end)
+                     (next-char-property-change (1+ (point)) end))))
+      (when first
+        (goto-char first)
+        (condition-case nil
+            (if (not
+                 (yes-or-no-p
+                  (substitute-command-keys
+                   (concat "Modify concealed text?  (\"no\" just aborts,"
+                           " \\[keyboard-quit] also reconceals) "))))
+                (progn (goto-char start)
+                       (error "Concealed-text change refused.")))
+          (quit (allout-flag-region ol-start ol-end nil)
+                (allout-flag-region ol-start ol-end t)
+                (error "Concealed-text change abandoned, text reconcealed."))))
+      (goto-char start))))
+;;;_  > allout-before-change-handler (beg end)
+(defun allout-before-change-handler (beg end)
+  "Protect against changes to invisible text.
+
+See allout-overlay-interior-modification-handler for details."
+
+  (if (and (allout-mode-p) undo-in-progress (allout-hidden-p))
+      (allout-show-to-offshoot))
+
+  ;; allout-overlay-interior-modification-handler on an overlay handles
+  ;; this in other emacs, via `allout-exposure-category's 'modification-hooks.
+  (when (and (featurep 'xemacs) (allout-mode-p))
+    ;; process all of the pending overlays:
+    (save-excursion
+      (goto-char beg)
+      (let ((overlay (allout-get-invisibility-overlay)))
+       (allout-overlay-interior-modification-handler
+        overlay nil beg end nil)))))
+;;;_  > allout-isearch-end-handler (&optional overlay)
+(defun allout-isearch-end-handler (&optional overlay)
+  "Reconcile allout outline exposure on arriving in hidden text after isearch.
+
+Optional OVERLAY parameter is for when this function is used by
+`isearch-open-invisible' overlay property.  It is otherwise unused, so this
+function can also be used as an `isearch-mode-end-hook'."
+
+  (if (and (allout-mode-p) (allout-hidden-p))
+      (allout-show-to-offshoot)))
+
 ;;;_ #3 Internal Position State-Tracking - "allout-recent-*" funcs
 ;;; All the basic outline functions that directly do string matches to
 ;;; evaluate heading prefix location set the variables
@@ -1395,19 +2240,35 @@ OPEN:   A topic that is not closed, though its offspring or body may be."
 (defvar allout-recent-prefix-end 0
   "Buffer point of the end of the last topic prefix encountered.")
 (make-variable-buffer-local 'allout-recent-prefix-end)
+;;;_  = allout-recent-depth
+(defvar allout-recent-depth 0
+  "Depth of the last topic prefix encountered.")
+(make-variable-buffer-local 'allout-recent-depth)
 ;;;_  = allout-recent-end-of-subtree
 (defvar allout-recent-end-of-subtree 0
   "Buffer point last returned by `allout-end-of-current-subtree'.")
 (make-variable-buffer-local 'allout-recent-end-of-subtree)
-;;;_  > allout-prefix-data (beg end)
-(defmacro allout-prefix-data (beginning end)
-  "Register allout-prefix state data - BEGINNING and END of prefix.
+;;;_  > allout-prefix-data ()
+(defsubst allout-prefix-data ()
+  "Register allout-prefix state data.
 
 For reference by `allout-recent' funcs.  Returns BEGINNING."
-  `(setq allout-recent-prefix-end ,end
-         allout-recent-prefix-beginning ,beginning))
+  (setq allout-recent-prefix-end (or (match-end 1) (match-end 2))
+        allout-recent-prefix-beginning (or (match-beginning 1)
+                                           (match-beginning 2))
+        allout-recent-depth (max 1 (- allout-recent-prefix-end
+                                      allout-recent-prefix-beginning
+                                      allout-header-subtraction)))
+  allout-recent-prefix-beginning)
+;;;_  > nullify-allout-prefix-data ()
+(defsubst nullify-allout-prefix-data ()
+  "Mark allout prefix data as being uninformative."
+  (setq allout-recent-prefix-end (point)
+        allout-recent-prefix-beginning (point)
+        allout-recent-depth 0)
+  allout-recent-prefix-beginning)
 ;;;_  > allout-recent-depth ()
-(defmacro allout-recent-depth ()
+(defsubst allout-recent-depth ()
   "Return depth of last heading encountered by an outline maneuvering function.
 
 All outline functions which directly do string matches to assess
@@ -1415,19 +2276,17 @@ headings set the variables `allout-recent-prefix-beginning' and
 `allout-recent-prefix-end' if successful.  This function uses those settings
 to return the current depth."
 
-  '(max 1 (- allout-recent-prefix-end
-            allout-recent-prefix-beginning
-            allout-header-subtraction)))
+  allout-recent-depth)
 ;;;_  > allout-recent-prefix ()
-(defmacro allout-recent-prefix ()
+(defsubst allout-recent-prefix ()
   "Like `allout-recent-depth', but returns text of last encountered prefix.
 
 All outline functions which directly do string matches to assess
 headings set the variables `allout-recent-prefix-beginning' and
 `allout-recent-prefix-end' if successful.  This function uses those settings
-to return the current depth."
-  '(buffer-substring allout-recent-prefix-beginning
-                    allout-recent-prefix-end))
+to return the current prefix."
+  (buffer-substring-no-properties allout-recent-prefix-beginning
+                                  allout-recent-prefix-end))
 ;;;_  > allout-recent-bullet ()
 (defmacro allout-recent-bullet ()
   "Like allout-recent-prefix, but returns bullet of last encountered prefix.
@@ -1436,63 +2295,120 @@ All outline functions which directly do string matches to assess
 headings set the variables `allout-recent-prefix-beginning' and
 `allout-recent-prefix-end' if successful.  This function uses those settings
 to return the current depth of the most recently matched topic."
-  '(buffer-substring (1- allout-recent-prefix-end)
-                    allout-recent-prefix-end))
+  '(buffer-substring-no-properties (1- allout-recent-prefix-end)
+                                   allout-recent-prefix-end))
 
 ;;;_ #4 Navigation
 
 ;;;_  - Position Assessment
 ;;;_   : Location Predicates
+;;;_    > allout-do-doublecheck ()
+(defsubst allout-do-doublecheck ()
+  "True if current item conditions qualify for checking on topic aberrance."
+  (and
+   ;; presume integrity of outline and yanked content during yank - necessary,
+   ;; to allow for level disparity of yank location and yanked text:
+   (not allout-inhibit-aberrance-doublecheck)
+   ;; allout-doublecheck-at-and-shallower is ceiling for doublecheck:
+   (<= allout-recent-depth allout-doublecheck-at-and-shallower)))
+;;;_     > allout-aberrant-container-p ()
+(defun allout-aberrant-container-p ()
+  "True if topic, or next sibling with children, contains them discontinuously.
+
+Discontinuous means an immediate offspring that is nested more
+than one level deeper than the topic.
+
+If topic has no offspring, then the next sibling with offspring will
+determine whether or not this one is determined to be aberrant.
+
+If true, then the allout-recent-* settings are calibrated on the
+offspring that qaulifies it as aberrant, ie with depth that
+exceeds the topic by more than one."
+
+  ;; This is most clearly understood when considering standard-prefix-leader
+  ;; low-level topics, which can all too easily match text not intended as
+  ;; headers.  For example, any line with a leading '.' or '*' and lacking a
+  ;; following bullet qualifies without this protection.  (A sequence of
+  ;; them can occur naturally, eg a typical textual bullet list.)  We
+  ;; disqualify such low-level sequences when they are followed by a
+  ;; discontinuously contained child, inferring that the sequences are not
+  ;; actually connected with their prospective context.
+
+  (let ((depth (allout-depth))
+        (start-point (point))
+        done aberrant)
+    (save-match-data
+      (save-excursion
+        (while (and (not done)
+                    (re-search-forward allout-line-boundary-regexp nil 0))
+          (allout-prefix-data)
+          (goto-char allout-recent-prefix-beginning)
+          (cond
+           ;; sibling - continue:
+           ((eq allout-recent-depth depth))
+           ;; first offspring is excessive - aberrant:
+           ((> allout-recent-depth (1+ depth))
+            (setq done t aberrant t))
+           ;; next non-sibling is lower-depth - not aberrant:
+           (t (setq done t))))))
+    (if aberrant
+        aberrant
+      (goto-char start-point)
+      ;; recalibrate allout-recent-*
+      (allout-depth)
+      nil)))
 ;;;_    > allout-on-current-heading-p ()
 (defun allout-on-current-heading-p ()
   "Return non-nil if point is on current visible topics' header line.
 
 Actually, returns prefix beginning point."
   (save-excursion
-    (beginning-of-line)
-    (and (looking-at allout-regexp)
-        (allout-prefix-data (match-beginning 0) (match-end 0)))))
+    (allout-beginning-of-current-line)
+    (save-match-data
+      (and (looking-at allout-regexp)
+           (allout-prefix-data)
+           (or (not (allout-do-doublecheck))
+               (not (allout-aberrant-container-p)))))))
 ;;;_    > allout-on-heading-p ()
 (defalias 'allout-on-heading-p 'allout-on-current-heading-p)
 ;;;_    > allout-e-o-prefix-p ()
 (defun allout-e-o-prefix-p ()
   "True if point is located where current topic prefix ends, heading begins."
-  (and (save-excursion (beginning-of-line)
-                      (looking-at allout-regexp))
-       (= (point)(save-excursion (allout-end-of-prefix)(point)))))
-;;;_    > allout-hidden-p ()
-(defmacro allout-hidden-p ()
-  "True if point is in hidden text."
-  '(save-excursion
-     (and (re-search-backward "[\n\r]" () t)
-         (= ?\r (following-char)))))
-;;;_    > allout-visible-p ()
-(defmacro allout-visible-p ()
-  "True if point is not in hidden text."
-  (interactive)
-  '(not (allout-hidden-p)))
+  (and (save-match-data
+        (save-excursion (let ((inhibit-field-text-motion t))
+                          (beginning-of-line))
+                        (looking-at allout-regexp))
+       (= (point) (save-excursion (allout-end-of-prefix)(point))))))
 ;;;_   : Location attributes
 ;;;_    > allout-depth ()
-(defsubst allout-depth ()
-  "Like `allout-current-depth', but respects hidden as well as visible topics."
+(defun allout-depth ()
+  "Return depth of topic most immediately containing point.
+
+Return zero if point is not within any topic.
+
+Like `allout-current-depth', but respects hidden as well as visible topics."
   (save-excursion
-    (if (allout-goto-prefix)
-       (allout-recent-depth)
-      (progn
-       ;; Oops, no prefix, zero prefix data:
-       (allout-prefix-data (point)(point))
-       ;; ... and return 0:
-       0))))
+    (let ((start-point (point)))
+      (if (and (allout-goto-prefix)
+               (not (< start-point (point))))
+          allout-recent-depth
+        (progn
+          ;; Oops, no prefix, nullify it:
+          (nullify-allout-prefix-data)
+          ;; ... and return 0:
+          0)))))
 ;;;_    > allout-current-depth ()
-(defmacro allout-current-depth ()
-  "Return nesting depth of visible topic most immediately containing point."
-  '(save-excursion
-     (if (allout-back-to-current-heading)
-        (max 1
-             (- allout-recent-prefix-end
-                allout-recent-prefix-beginning
-                allout-header-subtraction))
-       0)))
+(defun allout-current-depth ()
+  "Return depth of visible topic most immediately containing point.
+
+Return zero if point is not within any topic."
+  (save-excursion
+    (if (allout-back-to-current-heading)
+        (max 1
+             (- allout-recent-prefix-end
+                allout-recent-prefix-beginning
+                allout-header-subtraction))
+      0)))
 ;;;_    > allout-get-current-prefix ()
 (defun allout-get-current-prefix ()
   "Topic prefix of the current topic."
@@ -1508,13 +2424,13 @@ Actually, returns prefix beginning point."
 ;;;_    > allout-current-bullet ()
 (defun allout-current-bullet ()
   "Return bullet of current (visible) topic heading, or none if none found."
-  (condition-case err
+  (condition-case nil
       (save-excursion
        (allout-back-to-current-heading)
-       (buffer-substring (- allout-recent-prefix-end 1)
-                         allout-recent-prefix-end))
+       (buffer-substring-no-properties (- allout-recent-prefix-end 1)
+                                        allout-recent-prefix-end))
     ;; Quick and dirty provision, ostensibly for missing bullet:
-    ('args-out-of-range nil))
+    (args-out-of-range nil))
   )
 ;;;_    > allout-get-prefix-bullet (prefix)
 (defun allout-get-prefix-bullet (prefix)
@@ -1522,7 +2438,7 @@ Actually, returns prefix beginning point."
   ;; Doesn't make sense if we're old-style prefixes, but this just
   ;; oughtn't be called then, so forget about it...
   (if (string-match allout-regexp prefix)
-      (substring prefix (1- (match-end 0)) (match-end 0))))
+      (substring prefix (1- (match-end 2)) (match-end 2))))
 ;;;_    > allout-sibling-index (&optional depth)
 (defun allout-sibling-index (&optional depth)
   "Item number of this prospective topic among its siblings.
@@ -1534,12 +2450,12 @@ If less than this depth, ascend to that depth and count..."
 
   (save-excursion
     (cond ((and depth (<= depth 0) 0))
-          ((or (not depth) (= depth (allout-depth)))
+          ((or (null depth) (= depth (allout-depth)))
            (let ((index 1))
-             (while (allout-previous-sibling (allout-recent-depth) nil)
+             (while (allout-previous-sibling allout-recent-depth nil)
               (setq index (1+ index)))
              index))
-          ((< depth (allout-recent-depth))
+          ((< depth allout-recent-depth)
            (allout-ascend-to-depth depth)
            (allout-sibling-index))
           (0))))
@@ -1557,79 +2473,193 @@ Outermost is first."
     rev-sibls)
   )
 
-;;;_  - Navigation macros
+;;;_  - Navigation routines
+;;;_   > allout-beginning-of-current-line ()
+(defun allout-beginning-of-current-line ()
+  "Like beginning of line, but to visible text."
+
+  ;; This combination of move-beginning-of-line and beginning-of-line is
+  ;; deliberate, but the (beginning-of-line) may now be superfluous.
+  (let ((inhibit-field-text-motion t))
+    (move-beginning-of-line 1)
+    (beginning-of-line)
+    (while (and (not (bobp)) (or (not (bolp)) (allout-hidden-p)))
+      (beginning-of-line)
+      (if (or (allout-hidden-p) (not (bolp)))
+          (forward-char -1)))))
+;;;_   > allout-end-of-current-line ()
+(defun allout-end-of-current-line ()
+  "Move to the end of line, past concealed text if any."
+  ;; XXX This is for symmetry with `allout-beginning-of-current-line' -
+  ;; `move-end-of-line' doesn't suffer the same problem as
+  ;; `move-beginning-of-line'.
+  (let ((inhibit-field-text-motion t))
+    (end-of-line)
+    (while (allout-hidden-p)
+      (end-of-line)
+      (if (allout-hidden-p) (forward-char 1)))))
+;;;_   > allout-beginning-of-line ()
+(defun allout-beginning-of-line ()
+  "Beginning-of-line with `allout-beginning-of-line-cycles' behavior, if set."
+
+  (interactive)
+
+  (if (or (not allout-beginning-of-line-cycles)
+          (not (equal last-command this-command)))
+      (progn
+        (if (and (not (bolp))
+                 (allout-hidden-p (1- (point))))
+            (goto-char (previous-single-char-property-change
+                        (1- (point)) 'invisible)))
+        (move-beginning-of-line 1))
+    (allout-depth)
+    (let ((beginning-of-body
+           (save-excursion
+             (while (and (allout-do-doublecheck)
+                         (allout-aberrant-container-p)
+                         (allout-previous-visible-heading 1)))
+             (allout-beginning-of-current-entry)
+             (point))))
+      (cond ((= (current-column) 0)
+             (goto-char beginning-of-body))
+            ((< (point) beginning-of-body)
+             (allout-beginning-of-current-line))
+            ((= (point) beginning-of-body)
+             (goto-char (allout-current-bullet-pos)))
+            (t (allout-beginning-of-current-line)
+               (if (< (point) beginning-of-body)
+                   ;; we were on the headline after its start:
+                   (goto-char beginning-of-body)))))))
+;;;_   > allout-end-of-line ()
+(defun allout-end-of-line ()
+  "End-of-line with `allout-end-of-line-cycles' behavior, if set."
+
+  (interactive)
+
+  (if (or (not allout-end-of-line-cycles)
+          (not (equal last-command this-command)))
+      (allout-end-of-current-line)
+    (let ((end-of-entry (save-excursion
+                          (allout-end-of-entry)
+                          (point))))
+      (cond ((not (eolp))
+             (allout-end-of-current-line))
+            ((or (allout-hidden-p) (save-excursion
+                                     (forward-char -1)
+                                     (allout-hidden-p)))
+             (allout-back-to-current-heading)
+             (allout-show-current-entry)
+             (allout-show-children)
+             (allout-end-of-entry))
+            ((>= (point) end-of-entry)
+             (allout-back-to-current-heading)
+             (allout-end-of-current-line))
+            (t
+             (if (not (and transient-mark-mode mark-active))
+                 (push-mark))
+             (allout-end-of-entry))))))
 ;;;_   > allout-next-heading ()
 (defsubst allout-next-heading ()
-  "Move to the heading for the topic \(possibly invisible) before this one.
+  "Move to the heading for the topic (possibly invisible) after this one.
 
-Returns the location of the heading, or nil if none found."
+Returns the location of the heading, or nil if none found.
 
-  (if (and (bobp) (not (eobp)))
-       (forward-char 1))
+We skip anomolous low-level topics, a la `allout-aberrant-container-p'."
+  (save-match-data
 
-  (if (re-search-forward allout-line-boundary-regexp nil 0)
-      (allout-prefix-data              ; Got valid location state - set vars:
-       (goto-char (or (match-beginning 2)
-                     allout-recent-prefix-beginning))
-       (or (match-end 2) allout-recent-prefix-end))))
-;;;_   : allout-this-or-next-heading
+    (if (looking-at allout-regexp)
+        (forward-char 1))
+
+    (when (re-search-forward allout-line-boundary-regexp nil 0)
+      (allout-prefix-data)
+      (and (allout-do-doublecheck)
+           ;; this will set allout-recent-* on the first non-aberrant topic,
+           ;; whether it's the current one or one that disqualifies it:
+           (allout-aberrant-container-p))
+      (goto-char allout-recent-prefix-beginning))))
+;;;_   > allout-this-or-next-heading
 (defun allout-this-or-next-heading ()
   "Position cursor on current or next heading."
   ;; A throwaway non-macro that is defined after allout-next-heading
   ;; and usable by allout-mode.
-  (if (not (allout-goto-prefix)) (allout-next-heading)))
+  (if (not (allout-goto-prefix-doublechecked)) (allout-next-heading)))
 ;;;_   > allout-previous-heading ()
-(defmacro allout-previous-heading ()
-  "Move to the prior \(possibly invisible) heading line.
-
-Return the location of the beginning of the heading, or nil if not found."
-
-  '(if (bobp)
-       nil
-     (allout-goto-prefix)
-     (if
-        ;; searches are unbounded and return nil if failed:
-        (or (re-search-backward allout-line-boundary-regexp nil 0)
-            (looking-at allout-bob-regexp))
-        (progn                         ; Got valid location state - set vars:
-          (allout-prefix-data
-           (goto-char (or (match-beginning 2)
-                          allout-recent-prefix-beginning))
-           (or (match-end 2) allout-recent-prefix-end))))))
+(defun allout-previous-heading ()
+  "Move to the prior (possibly invisible) heading line.
+
+Return the location of the beginning of the heading, or nil if not found.
+
+We skip anomolous low-level topics, a la `allout-aberrant-container-p'."
+
+  (if (bobp)
+      nil
+    (let ((start-point (point)))
+      ;; allout-goto-prefix-doublechecked calls us, so we can't use it here.
+      (allout-goto-prefix)
+      (save-match-data
+        (when (or (re-search-backward allout-line-boundary-regexp nil 0)
+                  (looking-at allout-bob-regexp))
+          (goto-char (allout-prefix-data))
+          (if (and (allout-do-doublecheck)
+                   (allout-aberrant-container-p))
+              (or (allout-previous-heading)
+                  (and (goto-char start-point)
+                       ;; recalibrate allout-recent-*:
+                       (allout-depth)
+                       nil))
+            (point)))))))
+;;;_   > allout-get-invisibility-overlay ()
+(defun allout-get-invisibility-overlay ()
+  "Return the overlay at point that dictates allout invisibility."
+  (let ((overlays (overlays-at (point)))
+        got)
+    (while (and overlays (not got))
+      (if (equal (overlay-get (car overlays) 'invisible) 'allout)
+          (setq got (car overlays))
+        (pop overlays)))
+    got))
+;;;_   > allout-back-to-visible-text ()
+(defun allout-back-to-visible-text ()
+  "Move to most recent prior character that is visible, and return point."
+    (if (allout-hidden-p)
+      (goto-char (overlay-start (allout-get-invisibility-overlay))))
+    (point))
 
 ;;;_  - Subtree Charting
 ;;;_   " These routines either produce or assess charts, which are
 ;;; nested lists of the locations of topics within a subtree.
 ;;;
-;;; Use of charts enables efficient navigation of subtrees, by
-;;; requiring only a single regexp-search based traversal, to scope
-;;; out the subtopic locations.  The chart then serves as the basis
-;;; for assessment or adjustment of the subtree, without redundant
-;;; traversal of the structure.
-
-;;;_   > allout-chart-subtree (&optional levels orig-depth prev-depth)
-(defun allout-chart-subtree (&optional levels orig-depth prev-depth)
+;;; Charts enable efficient subtree navigation by providing a reusable basis
+;;; for elaborate, compound assessment and adjustment of a subtree.
+
+;;;_   > allout-chart-subtree (&optional levels visible orig-depth prev-depth)
+(defun allout-chart-subtree (&optional levels visible orig-depth prev-depth)
   "Produce a location \"chart\" of subtopics of the containing topic.
 
-Optional argument LEVELS specifies the depth \(relative to start
-depth) for the chart.
+Optional argument LEVELS specifies a depth limit (relative to start
+depth) for the chart.  Null LEVELS means no limit.
+
+When optional argument VISIBLE is non-nil, the chart includes
+only the visible subelements of the charted subjects.
+
+The remaining optional args are for internal use by the function.
+
+Point is left at the end of the subtree.
 
-Charts are used to capture outline structure, so that outline altering
+Charts are used to capture outline structure, so that outline-altering
 routines need assess the structure only once, and then use the chart
 for their elaborate manipulations.
 
-Topics are entered in the chart so the last one is at the car.
-The entry for each topic consists of an integer indicating the point
-at the beginning of the topic.  Charts for offspring consists of a
-list containing, recursively, the charts for the respective subtopics.
-The chart for a topics' offspring precedes the entry for the topic
-itself.
+The chart entries for the topics are in reverse order, so the
+last topic is listed first.  The entry for each topic consists of
+an integer indicating the point at the beginning of the topic
+prefix.  Charts for offspring consists of a list containing,
+recursively, the charts for the respective subtopics.  The chart
+for a topics' offspring precedes the entry for the topic itself.
 
-\(fn &optional LEVELS)"
-
-  ;; The other function parameters are for internal recursion, and should
-  ;; not be specified by external callers.  ORIG-DEPTH is depth of topic at
-  ;; starting point, and PREV-DEPTH is depth of prior topic."
+The other function parameters are for internal recursion, and should
+not be specified by external callers.  ORIG-DEPTH is depth of topic at
+starting point, and PREV-DEPTH is depth of prior topic."
 
   (let ((original (not orig-depth))    ; `orig-depth' set only in recursion.
        chart curr-depth)
@@ -1639,7 +2669,9 @@ itself.
                                        ; position to first offspring:
        (progn (setq orig-depth (allout-depth))
               (or prev-depth (setq prev-depth (1+ orig-depth)))
-              (allout-next-heading)))
+               (if visible
+                   (allout-next-visible-heading 1)
+                 (allout-next-heading))))
 
     ;; Loop over the current levels' siblings.  Besides being more
     ;; efficient than tail-recursing over a level, it avoids exceeding
@@ -1650,19 +2682,23 @@ itself.
 
     (while (and (not (eobp))
                                        ; Still within original topic?
-               (< orig-depth (setq curr-depth (allout-recent-depth)))
+               (< orig-depth (setq curr-depth allout-recent-depth))
                (cond ((= prev-depth curr-depth)
                       ;; Register this one and move on:
-                      (setq chart (cons (point) chart))
+                      (setq chart (cons allout-recent-prefix-beginning chart))
                       (if (and levels (<= levels 1))
                           ;; At depth limit - skip sublevels:
                           (or (allout-next-sibling curr-depth)
                               ;; or no more siblings - proceed to
                               ;; next heading at lesser depth:
                               (while (and (<= curr-depth
-                                              (allout-recent-depth))
-                                          (allout-next-heading))))
-                        (allout-next-heading)))
+                                              allout-recent-depth)
+                                           (if visible
+                                               (allout-next-visible-heading 1)
+                                             (allout-next-heading)))))
+                         (if visible
+                             (allout-next-visible-heading 1)
+                           (allout-next-heading))))
 
                      ((and (< prev-depth curr-depth)
                            (or (not levels)
@@ -1671,8 +2707,9 @@ itself.
                       (setq chart
                             (cons (allout-chart-subtree (and levels
                                                               (1- levels))
-                                                         orig-depth
-                                                         curr-depth)
+                                                         visible
+                                                         orig-depth
+                                                         curr-depth)
                                   chart))
                       ;; ... then continue with this one.
                       )
@@ -1685,11 +2722,11 @@ itself.
                                        ; the original level.  Position
                                        ; to the end of it:
        (progn (and (not (eobp)) (forward-char -1))
-              (and (memq (preceding-char) '(?\n ?\r))
-                   (memq (aref (buffer-substring (max 1 (- (point) 3))
-                                                 (point))
-                               1)
-                         '(?\n ?\r))
+              (and (= (preceding-char) ?\n)
+                   (= (aref (buffer-substring (max 1 (- (point) 3))
+                                               (point))
+                             1)
+                       ?\n)
                    (forward-char -1))
               (setq allout-recent-end-of-subtree (point))))
 
@@ -1702,38 +2739,41 @@ itself.
 Effectively a top-level chart of siblings.  See `allout-chart-subtree'
 for an explanation of charts."
   (save-excursion
-    (if (allout-goto-prefix)
-       (let ((chart (list (point))))
-         (while (allout-next-sibling)
-           (setq chart (cons (point) chart)))
-         (if chart (setq chart (nreverse chart)))))))
+    (when (allout-goto-prefix-doublechecked)
+      (let ((chart (list (point))))
+        (while (allout-next-sibling)
+          (setq chart (cons (point) chart)))
+        (if chart (setq chart (nreverse chart)))))))
 ;;;_   > allout-chart-to-reveal (chart depth)
 (defun allout-chart-to-reveal (chart depth)
 
   "Return a flat list of hidden points in subtree CHART, up to DEPTH.
 
+If DEPTH is nil, include hidden points at any depth.
+
 Note that point can be left at any of the points on chart, or at the
 start point."
 
   (let (result here)
-    (while (and (or (eq depth t) (> depth 0))
+    (while (and (or (null depth) (> depth 0))
                chart)
       (setq here (car chart))
       (if (listp here)
-         (let ((further (allout-chart-to-reveal here (or (eq depth t)
-                                                          (1- depth)))))
+         (let ((further (allout-chart-to-reveal here (if (null depth)
+                                                          depth
+                                                        (1- depth)))))
            ;; We're on the start of a subtree - recurse with it, if there's
            ;; more depth to go:
            (if further (setq result (append further result)))
            (setq chart (cdr chart)))
        (goto-char here)
-       (if (= (preceding-char) ?\r)
+        (if (allout-hidden-p)
            (setq result (cons here result)))
        (setq chart (cdr chart))))
     result))
 ;;;_   X allout-chart-spec (chart spec &optional exposing)
 ;; (defun allout-chart-spec (chart spec &optional exposing)
-;;   "Not yet \(if ever) implemented.
+;;   "Not yet (if ever) implemented.
 
 ;; Produce exposure directives given topic/subtree CHART and an exposure SPEC.
 
@@ -1754,7 +2794,7 @@ start point."
 ;;  - bare positive values indicate that this topic header should be
 ;;    opened.
 ;;  - Lists signify the beginning and end points of regions that should
-;;    be flagged, and the flag to employ.  (For concealment: `\(\?r\)', and
+;;    be flagged, and the flag to employ.  (For concealment: `(\?r)', and
 ;;    exposure:"
 ;;   (while spec
 ;;     (cond ((listp spec)
@@ -1774,20 +2814,34 @@ Not sensitive to topic visibility.
 
 Returns the point at the beginning of the prefix, or nil if none."
 
-  (let (done)
-    (while (and (not done)
-               (re-search-backward "[\n\r]" nil 1))
-      (forward-char 1)
-      (if (looking-at allout-regexp)
-         (setq done (allout-prefix-data (match-beginning 0)
-                                         (match-end 0)))
-       (forward-char -1)))
-    (if (bobp)
-       (cond ((looking-at allout-regexp)
-              (allout-prefix-data (match-beginning 0)(match-end 0)))
-             ((allout-next-heading))
-             (done))
-      done)))
+  (save-match-data
+    (let (done)
+      (while (and (not done)
+                  (search-backward "\n" nil 1))
+        (forward-char 1)
+        (if (looking-at allout-regexp)
+            (setq done (allout-prefix-data))
+          (forward-char -1)))
+      (if (bobp)
+          (cond ((looking-at allout-regexp)
+                 (allout-prefix-data))
+                ((allout-next-heading))
+                (done))
+        done))))
+;;;_   > allout-goto-prefix-doublechecked ()
+(defun allout-goto-prefix-doublechecked ()
+  "Put point at beginning of immediately containing outline topic.
+
+Like `allout-goto-prefix', but shallow topics (according to
+`allout-doublecheck-at-and-shallower') are checked and
+disqualified for child containment discontinuity, according to
+`allout-aberrant-container-p'."
+  (if (allout-goto-prefix)
+      (if (and (allout-do-doublecheck)
+               (allout-aberrant-container-p))
+          (allout-previous-heading)
+        (point))))
+
 ;;;_   > allout-end-of-prefix ()
 (defun allout-end-of-prefix (&optional ignore-decorations)
   "Position cursor at beginning of header text.
@@ -1795,109 +2849,182 @@ Returns the point at the beginning of the prefix, or nil if none."
 If optional IGNORE-DECORATIONS is non-nil, put just after bullet,
 otherwise skip white space between bullet and ensuing text."
 
-  (if (not (allout-goto-prefix))
+  (if (not (allout-goto-prefix-doublechecked))
       nil
-    (let ((match-data (match-data)))
-      (goto-char (match-end 0))
+    (goto-char allout-recent-prefix-end)
+    (save-match-data
       (if ignore-decorations
-         t
-       (while (looking-at "[0-9]") (forward-char 1))
-       (if (and (not (eolp)) (looking-at "\\s-")) (forward-char 1)))
-      (store-match-data match-data))
+          t
+        (while (looking-at "[0-9]") (forward-char 1))
+        (if (and (not (eolp)) (looking-at "\\s-")) (forward-char 1))))
     ;; Reestablish where we are:
     (allout-current-depth)))
 ;;;_   > allout-current-bullet-pos ()
 (defun allout-current-bullet-pos ()
-  "Return position of current \(visible) topic's bullet."
+  "Return position of current (visible) topic's bullet."
 
- (if (not (allout-current-depth))
 (if (not (allout-current-depth))
       nil
-   (1- (match-end 0))))
+    (1- allout-recent-prefix-end)))
 ;;;_   > allout-back-to-current-heading ()
 (defun allout-back-to-current-heading ()
-  "Move to heading line of current topic, or beginning if already on the line."
-
-  (beginning-of-line)
-  (prog1 (or (allout-on-current-heading-p)
-             (and (re-search-backward (concat "^\\(" allout-regexp "\\)")
-                                      nil
-                                      'move)
-                  (allout-prefix-data (match-beginning 1)(match-end 1))))
-    (if (interactive-p) (allout-end-of-prefix))))
+  "Move to heading line of current topic, or beginning if not in a topic.
+
+If interactive, we position at the end of the prefix.
+
+Return value of resulting point, unless we started outside
+of (before any) topics, in which case we return nil."
+
+  (allout-beginning-of-current-line)
+  (let ((bol-point (point)))
+    (if (allout-goto-prefix-doublechecked)
+        (if (<= (point) bol-point)
+            (if (interactive-p)
+                (allout-end-of-prefix)
+              (point))
+          (goto-char (point-min))
+          nil))))
 ;;;_   > allout-back-to-heading ()
 (defalias 'allout-back-to-heading 'allout-back-to-current-heading)
-;;;_   > allout-pre-next-preface ()
-(defun allout-pre-next-preface ()
+;;;_   > allout-pre-next-prefix ()
+(defun allout-pre-next-prefix ()
   "Skip forward to just before the next heading line.
 
 Returns that character position."
 
-  (if (re-search-forward allout-line-boundary-regexp nil 'move)
-      (prog1 (goto-char (match-beginning 0))
-             (allout-prefix-data (match-beginning 2)(match-end 2)))))
-;;;_   > allout-end-of-current-subtree ()
-(defun allout-end-of-current-subtree ()
-  "Put point at the end of the last leaf in the currently visible topic."
-  (interactive)
-  (allout-back-to-current-heading)
-  (let ((level (allout-recent-depth)))
+  (if (allout-next-heading)
+      (goto-char (1- allout-recent-prefix-beginning))))
+;;;_   > allout-end-of-subtree (&optional current include-trailing-blank)
+(defun allout-end-of-subtree (&optional current include-trailing-blank)
+  "Put point at the end of the last leaf in the containing topic.
+
+Optional CURRENT means put point at the end of the containing
+visible topic.
+
+Optional INCLUDE-TRAILING-BLANK means include a trailing blank line, if
+any, as part of the subtree.  Otherwise, that trailing blank will be
+excluded as delimiting whitespace between topics.
+
+Returns the value of point."
+  (interactive "P")
+  (if current
+      (allout-back-to-current-heading)
+    (allout-goto-prefix-doublechecked))
+  (let ((level allout-recent-depth))
     (allout-next-heading)
     (while (and (not (eobp))
-                (> (allout-recent-depth) level))
+                (> allout-recent-depth level))
       (allout-next-heading))
-    (and (not (eobp)) (forward-char -1))
-    (and (memq (preceding-char) '(?\n ?\r))
-         (memq (aref (buffer-substring (max 1 (- (point) 3)) (point)) 1)
-               '(?\n ?\r))
+    (if (eobp)
+        (allout-end-of-entry)
+      (forward-char -1))
+    (if (and (not include-trailing-blank) (= ?\n (preceding-char)))
          (forward-char -1))
     (setq allout-recent-end-of-subtree (point))))
+;;;_   > allout-end-of-current-subtree (&optional include-trailing-blank)
+(defun allout-end-of-current-subtree (&optional include-trailing-blank)
+
+  "Put point at end of last leaf in currently visible containing topic.
+
+Optional INCLUDE-TRAILING-BLANK means include a trailing blank line, if
+any, as part of the subtree.  Otherwise, that trailing blank will be
+excluded as delimiting whitespace between topics.
+
+Returns the value of point."
+  (interactive)
+  (allout-end-of-subtree t include-trailing-blank))
 ;;;_   > allout-beginning-of-current-entry ()
 (defun allout-beginning-of-current-entry ()
-  "When not already there, position point at beginning of current topic's body.
+  "When not already there, position point at beginning of current topic header.
 
 If already there, move cursor to bullet for hot-spot operation.
-\(See `allout-mode' doc string for details on hot-spot operation.)"
+\(See `allout-mode' doc string for details of hot-spot operation.)"
   (interactive)
   (let ((start-point (point)))
+    (move-beginning-of-line 1)
+    (if (< 0 (allout-current-depth))
+        (goto-char allout-recent-prefix-end)
+      (goto-char (point-min)))
     (allout-end-of-prefix)
     (if (and (interactive-p)
             (= (point) start-point))
        (goto-char (allout-current-bullet-pos)))))
-;;;_   > allout-end-of-current-entry ()
-(defun allout-end-of-current-entry ()
-  "Position the point at the end of the current topics' entry."
+;;;_   > allout-end-of-entry (&optional inclusive)
+(defun allout-end-of-entry (&optional inclusive)
+  "Position the point at the end of the current topics' entry.
+
+Optional INCLUSIVE means also include trailing empty line, if any.  When
+unset, whitespace between items separates them even when the items are
+collapsed."
   (interactive)
-  (allout-show-entry)
-  (prog1 (allout-pre-next-preface)
-    (if (and (not (bobp))(looking-at "^$"))
-        (forward-char -1))))
+  (allout-pre-next-prefix)
+  (if (and (not inclusive) (not (bobp)) (= ?\n (preceding-char)))
+      (forward-char -1))
+  (point))
 ;;;_   > allout-end-of-current-heading ()
 (defun allout-end-of-current-heading ()
   (interactive)
   (allout-beginning-of-current-entry)
-  (forward-line -1)
-  (end-of-line))
+  (search-forward "\n" nil t)
+  (forward-char -1))
 (defalias 'allout-end-of-heading 'allout-end-of-current-heading)
+;;;_   > allout-get-body-text ()
+(defun allout-get-body-text ()
+  "Return the unmangled body text of the topic immediately containing point."
+  (save-excursion
+    (allout-end-of-prefix)
+    (if (not (search-forward "\n" nil t))
+        nil
+      (backward-char 1)
+      (let ((pre-body (point)))
+        (if (not pre-body)
+            nil
+          (allout-end-of-entry t)
+          (if (not (= pre-body (point)))
+              (buffer-substring-no-properties (1+ pre-body) (point))))
+        )
+      )
+    )
+  )
 
 ;;;_  - Depth-wise
 ;;;_   > allout-ascend-to-depth (depth)
 (defun allout-ascend-to-depth (depth)
   "Ascend to depth DEPTH, returning depth if successful, nil if not."
   (if (and (> depth 0)(<= depth (allout-depth)))
-      (let ((last-good (point)))
-        (while (and (< depth (allout-depth))
-                    (setq last-good (point))
-                    (allout-beginning-of-level)
-                    (allout-previous-heading)))
-        (if (= (allout-recent-depth) depth)
-            (progn (goto-char allout-recent-prefix-beginning)
-                   depth)
-          (goto-char last-good)))))
+      (let (last-ascended)
+        (while (and (< depth allout-recent-depth)
+                    (setq last-ascended (allout-ascend))))
+        (goto-char allout-recent-prefix-beginning)
+        (if (interactive-p) (allout-end-of-prefix))
+        (and last-ascended allout-recent-depth))))
 ;;;_   > allout-ascend ()
-(defun allout-ascend ()
-  "Ascend one level, returning t if successful, nil if not."
-  (if (allout-beginning-of-level)
-      (allout-previous-heading)))
+(defun allout-ascend (&optional dont-move-if-unsuccessful)
+  "Ascend one level, returning resulting depth if successful, nil if not.
+
+Point is left at the beginning of the level whether or not
+successful, unless optional DONT-MOVE-IF-UNSUCCESSFUL is set, in
+which case point is returned to its original starting location."
+  (if dont-move-if-unsuccessful
+      (setq dont-move-if-unsuccessful (point)))
+  (prog1
+      (if (allout-beginning-of-level)
+          (let ((bolevel (point))
+                (bolevel-depth allout-recent-depth))
+            (allout-previous-heading)
+            (cond ((< allout-recent-depth bolevel-depth)
+                   allout-recent-depth)
+                  ((= allout-recent-depth bolevel-depth)
+                   (if dont-move-if-unsuccessful
+                       (goto-char dont-move-if-unsuccessful))
+                   (allout-depth)
+                   nil)
+                  (t
+                   ;; some topic after very first is lower depth than first:
+                   (goto-char bolevel)
+                   (allout-depth)
+                   nil))))
+    (if (interactive-p) (allout-end-of-prefix))))
 ;;;_   > allout-descend-to-depth (depth)
 (defun allout-descend-to-depth (depth)
   "Descend to depth DEPTH within current topic.
@@ -1907,49 +3034,25 @@ Returning depth if successful, nil if not."
         (start-depth (allout-depth)))
     (while
         (and (> (allout-depth) 0)
-             (not (= depth (allout-recent-depth))) ; ... not there yet
+             (not (= depth allout-recent-depth)) ; ... not there yet
              (allout-next-heading)     ; ... go further
-             (< start-depth (allout-recent-depth)))) ; ... still in topic
+             (< start-depth allout-recent-depth))) ; ... still in topic
     (if (and (> (allout-depth) 0)
-             (= (allout-recent-depth) depth))
+             (= allout-recent-depth depth))
         depth
       (goto-char start-point)
       nil))
   )
-;;;_   > allout-up-current-level (arg &optional dont-complain)
-(defun allout-up-current-level (arg &optional dont-complain interactive)
-  "Move out ARG levels from current visible topic.
-
-Positions on heading line of containing topic.  Error if unable to
-ascend that far, or nil if unable to ascend but optional arg
-DONT-COMPLAIN is non-nil."
-  (interactive "p\np")
-  (allout-back-to-current-heading)
-  (let ((present-level (allout-recent-depth))
-       (last-good (point))
-       failed
-       return)
-    ;; Loop for iterating arg:
-    (while (and (> (allout-recent-depth) 1)
-                (> arg 0)
-                (not (bobp))
-               (not failed))
-      (setq last-good (point))
-      ;; Loop for going back over current or greater depth:
-      (while (and (not (< (allout-recent-depth) present-level))
-                 (or (allout-previous-visible-heading 1)
-                     (not (setq failed present-level)))))
-      (setq present-level (allout-current-depth))
-      (setq arg (- arg 1)))
-    (if (or failed
-           (> arg 0))
-       (progn (goto-char last-good)
-              (if interactive (allout-end-of-prefix))
-              (if (not dont-complain)
-                  (error "Can't ascend past outermost level")
-                (if interactive (allout-end-of-prefix))
-                nil))
-      (if interactive (allout-end-of-prefix))
+;;;_   > allout-up-current-level (arg)
+(defun allout-up-current-level (arg)
+  "Move out ARG levels from current visible topic."
+  (interactive "p")
+  (let ((start-point (point)))
+    (allout-back-to-current-heading)
+    (if (not (allout-ascend))
+        (progn (goto-char start-point)
+               (error "Can't ascend past outermost level"))
+      (if (interactive-p) (allout-end-of-prefix))
       allout-recent-prefix-beginning)))
 
 ;;;_  - Linear
@@ -1961,27 +3064,108 @@ Traverse at optional DEPTH, or current depth if none specified.
 
 Go backward if optional arg BACKWARD is non-nil.
 
-Return depth if successful, nil otherwise."
+Return the start point of the new topic if successful, nil otherwise."
 
-  (if (and backward (bobp))
+  (if (if backward (bobp) (eobp))
       nil
-    (let ((start-depth (or depth (allout-depth)))
+    (let ((target-depth (or depth (allout-depth)))
           (start-point (point))
+          (start-prefix-beginning allout-recent-prefix-beginning)
+          (count 0)
+          leaping
          last-depth)
-      (while (and (not (if backward (bobp) (eobp)))
-                  (if backward (allout-previous-heading)
-                    (allout-next-heading))
-                  (> (setq last-depth (allout-recent-depth)) start-depth)))
-      (if (and (not (eobp))
-               (and (> (or last-depth (allout-depth)) 0)
-                    (= (allout-recent-depth) start-depth)))
-          allout-recent-prefix-beginning
-        (goto-char start-point)
-       (if depth (allout-depth) start-depth)
-        nil))))
+      (while (and
+              ;; done too few single steps to resort to the leap routine:
+              (not leaping)
+              ;; not at limit:
+              (not (if backward (bobp) (eobp)))
+              ;; still traversable:
+              (if backward (allout-previous-heading) (allout-next-heading))
+              ;; we're below the target depth
+              (> (setq last-depth allout-recent-depth) target-depth))
+        (setq count (1+ count))
+        (if (> count 7)                 ; lists are commonly 7 +- 2, right?-)
+            (setq leaping t)))
+      (cond (leaping
+             (or (allout-next-sibling-leap target-depth backward)
+                 (progn
+                   (goto-char start-point)
+                   (if depth (allout-depth) target-depth)
+                   nil)))
+            ((and (not (eobp))
+                  (and (> (or last-depth (allout-depth)) 0)
+                       (= allout-recent-depth target-depth))
+                  (not (= start-prefix-beginning
+                          allout-recent-prefix-beginning)))
+             allout-recent-prefix-beginning)
+            (t
+             (goto-char start-point)
+             (if depth (allout-depth) target-depth)
+             nil)))))
+;;;_   > allout-next-sibling-leap (&optional depth backward)
+(defun allout-next-sibling-leap (&optional depth backward)
+  "Like `allout-next-sibling', but by direct search for topic at depth.
+
+Traverse at optional DEPTH, or current depth if none specified.
+
+Go backward if optional arg BACKWARD is non-nil.
+
+Return the start point of the new topic if successful, nil otherwise.
+
+Costs more than regular `allout-next-sibling' for short traversals:
+
+ - we have to check the prior (next, if travelling backwards)
+   item to confirm connectivity with the prior topic, and
+ - if confirmed, we have to reestablish the allout-recent-* settings with
+   some extra navigation
+ - if confirmation fails, we have to do more work to recover
+
+It is an increasingly big win when there are many intervening
+offspring before the next sibling, however, so
+`allout-next-sibling' resorts to this if it finds itself in that
+situation."
+
+  (if (if backward (bobp) (eobp))
+      nil
+    (let* ((start-point (point))
+           (target-depth (or depth (allout-depth)))
+           (search-whitespace-regexp nil)
+           (depth-biased (- target-depth 2))
+           (expression (if (<= target-depth 1)
+                           allout-depth-one-regexp
+                         (format allout-depth-specific-regexp
+                                 depth-biased depth-biased)))
+           found
+           done)
+      (while (not done)
+        (setq found (save-match-data
+                      (if backward
+                          (re-search-backward expression nil 'to-limit)
+                        (forward-char 1)
+                        (re-search-forward expression nil 'to-limit))))
+        (if (and found (allout-aberrant-container-p))
+            (setq found nil))
+        (setq done (or found (if backward (bobp) (eobp)))))
+      (if (not found)
+          (progn (goto-char start-point)
+                 nil)
+        ;; rationale: if any intervening items were at a lower depth, we
+        ;; would now be on the first offspring at the target depth - ie,
+        ;; the preceeding item (per the search direction) must be at a
+        ;; lesser depth.  that's all we need to check.
+        (if backward (allout-next-heading) (allout-previous-heading))
+        (if (< allout-recent-depth target-depth)
+            ;; return to start and reestablish allout-recent-*:
+            (progn
+              (goto-char start-point)
+              (allout-depth)
+              nil)
+          (goto-char found)
+          ;; locate cursor and set allout-recent-*:
+          (allout-goto-prefix))))))
 ;;;_   > allout-previous-sibling (&optional depth backward)
 (defun allout-previous-sibling (&optional depth backward)
-  "Like `allout-forward-current-level', but backwards & respect invisible topics.
+  "Like `allout-forward-current-level' backwards, respecting invisible topics.
 
 Optional DEPTH specifies depth to traverse, default current depth.
 
@@ -1998,11 +3182,9 @@ Presumes point is at the start of a topic prefix."
  (if (or (bobp) (eobp))
      nil
    (forward-char -1))
- (if (or (bobp) (not (memq (preceding-char) '(?\n ?\r))))
+ (if (or (bobp) (not (= ?\n (preceding-char))))
      nil
-   (forward-char -1)
-   (if (or (bobp) (not (memq (preceding-char) '(?\n ?\r))))
-       (forward-char -1)))
+   (forward-char -1))
  (point))
 ;;;_   > allout-beginning-of-level ()
 (defun allout-beginning-of-level ()
@@ -2014,34 +3196,50 @@ Presumes point is at the start of a topic prefix."
 
   (let ((depth (allout-depth)))
     (while (allout-previous-sibling depth nil))
-    (prog1 (allout-recent-depth)
-      (allout-end-of-prefix))))
+    (prog1 allout-recent-depth
+      (if (interactive-p) (allout-end-of-prefix)))))
 ;;;_   > allout-next-visible-heading (arg)
 (defun allout-next-visible-heading (arg)
   "Move to the next ARG'th visible heading line, backward if arg is negative.
 
-Move as far as possible in indicated direction \(beginning or end of
-buffer) if headings are exhausted."
+Move to buffer limit in indicated direction if headings are exhausted."
 
   (interactive "p")
-  (let* ((backward (if (< arg 0) (setq arg (* -1 arg))))
+  (let* ((inhibit-field-text-motion t)
+         (backward (if (< arg 0) (setq arg (* -1 arg))))
         (step (if backward -1 1))
-        (start-point (point))
         prev got)
 
-    (while (> arg 0)                   ; limit condition
-      (while (and (not (if backward (bobp)(eobp))) ; boundary condition
-                 ;; Move, skipping over all those concealed lines:
-                 (< -1 (forward-line step))
-                 (not (setq got (looking-at allout-regexp)))))
+    (while (> arg 0)
+      (while (and
+              ;; Boundary condition:
+              (not (if backward (bobp)(eobp)))
+              ;; Move, skipping over all concealed lines in one fell swoop:
+              (prog1 (condition-case nil (or (line-move step) t)
+                       (error nil))
+                (allout-beginning-of-current-line))
+              ;; Deal with apparent header line:
+              (save-match-data
+                (if (not (looking-at allout-regexp))
+                    ;; not a header line, keep looking:
+                    t
+                  (allout-prefix-data)
+                  (if (and (allout-do-doublecheck)
+                           (allout-aberrant-container-p))
+                      ;; skip this aberrant prospective header line:
+                      t
+                    ;; this prospective headerline qualifies - register:
+                    (setq got allout-recent-prefix-beginning)
+                    ;; and break the loop:
+                    nil)))))
       ;; Register this got, it may be the last:
       (if got (setq prev got))
       (setq arg (1- arg)))
     (cond (got                         ; Last move was to a prefix:
-          (allout-prefix-data (match-beginning 0) (match-end 0))
-          (allout-end-of-prefix))
+           (allout-end-of-prefix))
          (prev                         ; Last move wasn't, but prev was:
-          (allout-prefix-data (match-beginning 0) (match-end 0)))
+           (goto-char prev)
+           (allout-end-of-prefix))
          ((not backward) (end-of-line) nil))))
 ;;;_   > allout-previous-visible-heading (arg)
 (defun allout-previous-visible-heading (arg)
@@ -2051,55 +3249,42 @@ With argument, repeats or can move forward if negative.
 A heading line is one that starts with a `*' (or that `allout-regexp'
 matches)."
   (interactive "p")
-  (allout-next-visible-heading (- arg)))
+  (prog1 (allout-next-visible-heading (- arg))
+    (if (interactive-p) (allout-end-of-prefix))))
 ;;;_   > allout-forward-current-level (arg)
-(defun allout-forward-current-level (arg &optional interactive)
+(defun allout-forward-current-level (arg)
   "Position point at the next heading of the same level.
 
 Takes optional repeat-count, goes backward if count is negative.
 
 Returns resulting position, else nil if none found."
-  (interactive "p\np")
+  (interactive "p")
   (let ((start-depth (allout-current-depth))
-       (start-point (point))
        (start-arg arg)
-       (backward (> 0 arg))
-       last-depth
-       (last-good (point))
-       at-boundary)
+       (backward (> 0 arg)))
     (if (= 0 start-depth)
        (error "No siblings, not in a topic..."))
     (if backward (setq arg (* -1 arg)))
-    (while (not (or (zerop arg)
-                   at-boundary))
-      (while (and (not (if backward (bobp) (eobp)))
-                 (if backward (allout-previous-visible-heading 1)
-                   (allout-next-visible-heading 1))
-                 (> (setq last-depth (allout-recent-depth)) start-depth)))
-      (if (and last-depth (= last-depth start-depth)
-              (not (if backward (bobp) (eobp))))
-         (setq last-good (point)
-               arg (1- arg))
-       (setq at-boundary t)))
-    (if (and (not (eobp))
-            (= arg 0)
-            (and (> (or last-depth (allout-depth)) 0)
-                 (= (allout-recent-depth) start-depth)))
-       allout-recent-prefix-beginning
-      (goto-char last-good)
-      (if (not interactive)
-         nil
-       (allout-end-of-prefix)
-       (error "Hit %s level %d topic, traversed %d of %d requested"
-              (if backward "first" "last")
-              (allout-recent-depth)
-              (- (abs start-arg) arg)
-              (abs start-arg))))))
+    (allout-back-to-current-heading)
+    (while (and (not (zerop arg))
+                (if backward
+                    (allout-previous-sibling)
+                  (allout-next-sibling)))
+      (setq arg (1- arg)))
+    (if (not (interactive-p))
+        nil
+      (allout-end-of-prefix)
+      (if (not (zerop arg))
+          (error "Hit %s level %d topic, traversed %d of %d requested"
+                 (if backward "first" "last")
+                 allout-recent-depth
+                 (- (abs start-arg) arg)
+                 (abs start-arg))))))
 ;;;_   > allout-backward-current-level (arg)
-(defun allout-backward-current-level (arg &optional interactive)
+(defun allout-backward-current-level (arg)
   "Inverse of `allout-forward-current-level'."
-  (interactive "p\np")
-  (if interactive
+  (interactive "p")
+  (if (interactive-p)
       (let ((current-prefix-arg (* -1 arg)))
        (call-interactively 'allout-forward-current-level))
     (allout-forward-current-level (* -1 arg))))
@@ -2107,121 +3292,6 @@ Returns resulting position, else nil if none found."
 ;;;_ #5 Alteration
 
 ;;;_  - Fundamental
-;;;_   > allout-before-change-protect (beg end)
-(defun allout-before-change-protect (beg end)
-  "Outline before-change hook, regulates changes to concealed text.
-
-Reveal concealed text that would be changed by current command, and
-offer user choice to commit or forego the change.  Unchanged text is
-reconcealed.  User has option to have changed text reconcealed.
-
-Undo commands are specially treated - the user is not prompted for
-choice, the undoes are always committed (based on presumption that the
-things being undone were already subject to this regulation routine),
-and undoes always leave the changed stuff exposed.
-
-Changes to concealed regions are ignored while file is being written.
-\(This is for the sake of functions that do change the file during
-writes, like crypt and zip modes.)
-
-Locally bound in outline buffers to `before-change-functions', which
-in Emacs 19 is run before any change to the buffer.
-
-Any functions which set [`this-command' to `undo', or which set]
-`allout-override-protect' non-nil (as does, eg, allout-flag-chars)
-are exempt from this restriction."
-  (if (and (allout-mode-p)
-                                       ; allout-override-protect
-                                       ; set by functions that know what
-                                       ; they're doing, eg outline internals:
-          (not allout-override-protect)
-          (not allout-during-write-cue)
-          (save-match-data             ; Preserve operation position state.
-                                       ; Both beginning and end chars must
-                                       ; be exposed:
-            (save-excursion (if (memq this-command '(newline open-line))
-                                ;; Compensate for stupid Emacs {new,
-                                ;; open-}line display optimization:
-                                (setq beg (1+ beg)
-                                      end (1+ end)))
-                            (goto-char beg)
-                            (or (allout-hidden-p)
-                                (and (not (= beg end))
-                                     (goto-char end)
-                                     (allout-hidden-p))))))
-      (save-match-data
-       (if (equal this-command 'undo)
-                ;; Allow undo without inhibition.
-                ;; - Undoing new and open-line hits stupid Emacs redisplay
-                ;;   optimization (em 19 cmds.c, ~ line 200).
-                ;; - Presumably, undoing what was properly protected when
-                ;;   done.
-                ;; - Undo may be users' only recourse in protection faults.
-                ;; So, expose what getting changed:
-           (progn (message "Undo! - exposing concealed target...")
-                  (if (allout-hidden-p)
-                      (allout-show-children))
-                  (message "Undo!"))
-         (let (response
-               (rehide-completely (save-excursion (allout-goto-prefix)
-                                                  (allout-hidden-p)))
-               rehide-place)
-
-           (save-excursion
-             (if (condition-case err
-                     ;; Condition case to catch keyboard quits during reads.
-                     (progn
-                                       ; Give them a peek where
-                       (save-excursion
-                         (if (eolp) (setq rehide-place
-                                          (allout-goto-prefix)))
-                         (allout-show-entry))
-                                       ; Present the message, but...
-                                       ; leave the cursor at the location
-                                       ; until they respond:
-                                       ; Then interpret the response:
-                       (while
-                           (progn
-                             (message (concat "Change inside concealed"
-                                              " region - do it? "
-                                              "(n or 'y'/'r'eclose)"))
-                             (setq response (read-char))
-                             (not
-                              (cond ((memq response '(?r ?R))
-                                     (setq response 'reclose))
-                                    ((memq response '(?y ?Y ? ))
-                                     (setq response t))
-                                    ((memq response '(?n ?N 127))
-                                     (setq response nil)
-                                     t)
-                                    ((eq response ??)
-                                     (message
-                                      "`r' means `yes, then reclose'")
-                                     nil)
-                                    (t (message "Please answer y, n, or r")
-                                       (sit-for 1)
-                                       nil)))))
-                       response)
-                   ('quit nil))
-                                       ; Continue:
-                 (if (eq response 'reclose)
-                     (save-excursion
-                       (if rehide-place (goto-char rehide-place))
-                       (if rehide-completely
-                           (allout-hide-current-entry-completely)
-                         (allout-hide-current-entry)))
-                   (if (allout-ascend-to-depth (1- (allout-recent-depth)))
-                       (allout-show-children)
-                     (allout-show-to-offshoot)))
-                                       ; Prevent:
-               (if rehide-completely
-                   (save-excursion
-                     (if rehide-place (goto-char rehide-place))
-                     (allout-hide-current-entry-completely))
-                 (allout-hide-current-entry))
-               (error "Change within concealed region prevented"))))))
-    )  ; if
-  )    ; defun
 ;;;_   = allout-post-goto-bullet
 (defvar allout-post-goto-bullet nil
   "Outline internal var, for `allout-pre-command-business' hot-spot operation.
@@ -2232,57 +3302,31 @@ hot-spot operation, where literal characters typed over a topic bullet
 are mapped to the command of the corresponding control-key on the
 `allout-mode-map'.")
 (make-variable-buffer-local 'allout-post-goto-bullet)
+;;;_   = allout-command-counter
+(defvar allout-command-counter 0
+  "Counter that monotonically increases in allout-mode buffers.
+
+Set by `allout-pre-command-business', to support allout addons in
+coordinating with allout activity.")
+(make-variable-buffer-local 'allout-command-counter)
 ;;;_   > allout-post-command-business ()
 (defun allout-post-command-business ()
   "Outline `post-command-hook' function.
 
-- Null `allout-override-protect', so it's not left open.
-
 - Implement (and clear) `allout-post-goto-bullet', for hot-spot
   outline commands.
 
-- Massages `buffer-undo-list' so successive, standard character self-inserts
-  are aggregated.  This kludge compensates for lack of undo bunching when
-  `before-change-functions' is used."
+- Decrypt topic currently being edited if it was encrypted for a save."
 
                                        ; Apply any external change func:
   (if (not (allout-mode-p))            ; In allout-mode.
       nil
-    (setq allout-override-protect nil)
-    (if allout-isearch-dynamic-expose
-       (allout-isearch-rectification))
-    (if allout-during-write-cue
-       ;; Was used by allout-before-change-protect, done with it now:
-       (setq allout-during-write-cue nil))
-    ;; Undo bunching business:
-    (if (and (listp buffer-undo-list)  ; Undo history being kept.
-            (equal this-command 'self-insert-command)
-            (equal last-command 'self-insert-command))
-       (let* ((prev-stuff (cdr buffer-undo-list))
-              (before-prev-stuff (cdr (cdr prev-stuff)))
-              cur-cell cur-from cur-to
-              prev-cell prev-from prev-to)
-         (if (and before-prev-stuff    ; Goes back far enough to bother,
-                  (not (car prev-stuff)) ; and break before current,
-                  (not (car before-prev-stuff)) ; !and break before prev!
-                  (setq prev-cell (car (cdr prev-stuff))) ; contents now,
-                  (setq cur-cell (car buffer-undo-list)) ; contents prev.
-
-                  ;; cur contents denote a single char insertion:
-                  (numberp (setq cur-from (car cur-cell)))
-                  (numberp (setq cur-to (cdr cur-cell)))
-                  (= 1 (- cur-to cur-from))
-
-                  ;; prev contents denote fewer than aggregate-limit
-                  ;; insertions:
-                  (numberp (setq prev-from (car prev-cell)))
-                  (numberp (setq prev-to (cdr prev-cell)))
-                                       ; Below threshold:
-                  (> allout-undo-aggregation (- prev-to prev-from)))
-             (setq buffer-undo-list
-                   (cons (cons prev-from cur-to)
-                         (cdr (cdr (cdr buffer-undo-list))))))))
-    ;; Implement -post-goto-bullet, if set: (must be after undo business)
+
+    (if (and (boundp 'allout-after-save-decrypt)
+             allout-after-save-decrypt)
+        (allout-after-saves-handler))
+
+    ;; Implement allout-post-goto-bullet, if set:
     (if (and allout-post-goto-bullet
             (allout-current-bullet-pos))
        (progn (goto-char (allout-current-bullet-pos))
@@ -2291,7 +3335,9 @@ are mapped to the command of the corresponding control-key on the
 ;;;_   > allout-pre-command-business ()
 (defun allout-pre-command-business ()
   "Outline `pre-command-hook' function for outline buffers.
-Implements special behavior when cursor is on bullet character.
+
+Among other things, implements special behavior when the cursor is on the
+topic bullet character.
 
 When the cursor is on the bullet character, self-insert characters are
 reinterpreted as the corresponding control-character in the
@@ -2304,131 +3350,73 @@ outline maneuvering operations by positioning the cursor on the bullet
 char.  When in this mode you can use regular cursor-positioning
 command/keystrokes to relocate the cursor off of a bullet character to
 return to regular interpretation of self-insert characters."
+
   (if (not (allout-mode-p))
-      ;; Shouldn't be invoked if not in allout allout-mode, but just in case:
       nil
-    ;; Register isearch status:
-    (if (and (boundp  'isearch-mode) isearch-mode)
-       (setq allout-pre-was-isearching t)
-      (setq allout-pre-was-isearching nil))
-    ;; Hot-spot navigation provisions:
+    ;; Increment allout-command-counter
+    (setq allout-command-counter (1+ allout-command-counter))
+    ;; Do hot-spot navigation.
     (if (and (eq this-command 'self-insert-command)
             (eq (point)(allout-current-bullet-pos)))
-       (let* ((this-key-num (cond
-                             ((numberp last-command-char)
-                              last-command-char)
-                             ((fboundp 'char-to-int)
-                              (char-to-int last-command-char))
-                             (t 0)))
-              mapped-binding)
-         (if (zerop this-key-num)
-             nil
-                                       ; Map upper-register literals
-                                       ; to lower register:
-           (if (<= 96 this-key-num)
-               (setq this-key-num (- this-key-num 32)))
-                                       ; Check if we have a literal:
-           (if (and (<= 64 this-key-num)
-                    (>= 96 this-key-num))
-               (setq mapped-binding
-                     (lookup-key 'allout-mode-map
-                                 (concat allout-command-prefix
-                                         (char-to-string (- this-key-num
-                                                            64))))))
-           (if mapped-binding
-               (setq allout-post-goto-bullet t
-                     this-command mapped-binding)))))))
+        (allout-hotspot-key-handler))))
+;;;_   > allout-hotspot-key-handler ()
+(defun allout-hotspot-key-handler ()
+  "Catchall handling of key bindings in hot-spots.
+
+Translates unmodified keystrokes to corresponding allout commands, when
+they would qualify if prefixed with the allout-command-prefix, and sets
+this-command accordingly.
+
+Returns the qualifying command, if any, else nil."
+  (interactive)
+  (let* ((key-string (if (numberp last-command-char)
+                         (char-to-string last-command-char)))
+         (key-num (cond ((numberp last-command-char) last-command-char)
+                        ;; for XEmacs character type:
+                        ((and (fboundp 'characterp)
+                              (apply 'characterp (list last-command-char)))
+                         (apply 'char-to-int (list last-command-char)))
+                        (t 0)))
+         mapped-binding)
+
+    (if (zerop key-num)
+        nil
+
+      (if (and
+           ;; exclude control chars and escape:
+           (<= 33 key-num)
+           (setq mapped-binding
+                 (or (and (assoc key-string allout-keybindings-list)
+                          ;; translate literal membership on list:
+                          (cadr (assoc key-string allout-keybindings-list)))
+                     ;; translate as a keybinding:
+                     (key-binding (vconcat allout-command-prefix
+                                          (char-to-string
+                                           (if (and (<= 97 key-num)   ; "a"
+                                                    (>= 122 key-num)) ; "z"
+                                               (- key-num 96) key-num)))
+                                  t))))
+          ;; Qualified as an allout command - do hot-spot operation.
+          (setq allout-post-goto-bullet t)
+        ;; accept-defaults nil, or else we'll get allout-item-icon-key-handler.
+        (setq mapped-binding (key-binding (char-to-string key-num))))
+
+      (while (keymapp mapped-binding)
+        (setq mapped-binding
+              (lookup-key mapped-binding (vector (read-char)))))
+
+      (if mapped-binding
+          (setq this-command mapped-binding)))))
+
 ;;;_   > allout-find-file-hook ()
 (defun allout-find-file-hook ()
-  "Activate `allout-mode' when `allout-auto-activation' & `allout-layout' are non-nil.
+  "Activate `allout-mode' on non-nil `allout-auto-activation', `allout-layout'.
 
 See `allout-init' for setup instructions."
   (if (and allout-auto-activation
           (not (allout-mode-p))
           allout-layout)
       (allout-mode t)))
-;;;_   > allout-isearch-rectification
-(defun allout-isearch-rectification ()
-  "Rectify outline exposure before, during, or after isearch.
-
-Called as part of `allout-post-command-business'."
-
-  (let ((isearching isearch-mode))
-    (cond ((and isearching (not allout-pre-was-isearching))
-          (allout-isearch-expose 'start))
-         ((and isearching allout-pre-was-isearching)
-          (allout-isearch-expose 'continue))
-         ((and (not isearching) allout-pre-was-isearching)
-          (allout-isearch-expose 'final))
-         ;; Not and wasn't isearching:
-         (t (setq allout-isearch-prior-pos nil)))))
-;;;_   = allout-isearch-was-font-lock
-(defvar allout-isearch-was-font-lock
-  (and (boundp 'font-lock-mode) font-lock-mode))
-
-;;;_   > allout-flag-region (from to flag)
-(defmacro allout-flag-region (from to flag)
-  "Hide or show lines from FROM to TO, via Emacs `selective-display' FLAG char.
-Ie, text following flag C-m \(carriage-return) is hidden until the
-next C-j (newline) char.
-
-Returns the endpoint of the region."
-  `(let ((buffer-read-only nil)
-          (allout-override-protect t))
-       (subst-char-in-region ,from ,to
-                            (if (= ,flag ?\n) ?\r ?\n)
-                            ,flag t)))
-
-;;;_   > allout-isearch-expose (mode)
-(defun allout-isearch-expose (mode)
-  "MODE is either 'clear, 'start, 'continue, or 'final."
-  ;; allout-isearch-prior-pos encodes exposure status of prior pos:
-  ;; (pos was-vis header-pos end-pos)
-  ;; pos       - point of concern
-  ;; was-vis   - t, else 'topic if entire topic was exposed, 'entry otherwise
-  ;; Do reclosure or prior pos, as necessary:
-  (if (eq mode 'start)
-      (setq allout-isearch-was-font-lock (and (boundp 'font-lock-mode)
-                                               font-lock-mode)
-           font-lock-mode nil)
-    (if (eq mode 'final)
-       (setq font-lock-mode allout-isearch-was-font-lock))
-    (if (and allout-isearch-prior-pos
-            (listp allout-isearch-prior-pos))
-       ;; Conceal prior peek:
-       (allout-flag-region (car (cdr allout-isearch-prior-pos))
-                            (car (cdr (cdr allout-isearch-prior-pos)))
-                            ?\r)))
-  (if (allout-visible-p)
-      (setq allout-isearch-prior-pos nil)
-    (if (not (eq mode 'final))
-       (setq allout-isearch-prior-pos (cons (point) (allout-show-entry)))
-      (if isearch-mode-end-hook-quit
-         nil
-       (setq allout-isearch-prior-pos nil)
-       (allout-show-children)))))
-;;;_   > allout-enwrap-isearch ()
-(defun allout-enwrap-isearch ()
-  "Impose `isearch-abort' wrapper for dynamic exposure in isearch.
-
-The function checks to ensure that the rebinding is done only once."
-  (add-hook 'isearch-mode-end-hook 'allout-isearch-rectification))
-
-;;; Prevent unnecessary font-lock while isearching!
-(defvar isearch-was-font-locking nil)
-(defun isearch-inhibit-font-lock ()
-  "Inhibit `font-lock-mode' while isearching - for use on `isearch-mode-hook'."
-  (if (and (allout-mode-p) (boundp 'font-lock-mode) font-lock-mode)
-      (setq isearch-was-font-locking t
-           font-lock-mode nil)))
-(add-hook 'isearch-mode-hook 'isearch-inhibit-font-lock)
-(defun isearch-reenable-font-lock ()
-  "Reenable font-lock after isearching - for use on `isearch-mode-end-hook'."
-  (if (and (boundp 'font-lock-mode) font-lock-mode)
-      (if (and (allout-mode-p) isearch-was-font-locking)
-         (setq isearch-was-font-locking nil
-               font-lock-mode t))))
-(add-hook 'isearch-mode-end-hook 'isearch-reenable-font-lock)
 
 ;;;_  - Topic Format Assessment
 ;;;_   > allout-solicit-alternate-bullet (depth &optional current-bullet)
@@ -2447,7 +3435,7 @@ Offer one suitable for current depth DEPTH as default."
       (setq choice (solicit-char-in-string
                     (format "Select bullet: %s ('%s' default): "
                             sans-escapes
-                            default-bullet)
+                            (substring-no-properties default-bullet))
                     sans-escapes
                     t)))
     (message "")
@@ -2465,6 +3453,14 @@ Offer one suitable for current depth DEPTH as default."
                  (if prefix
                      (allout-get-prefix-bullet prefix)
                    (allout-get-bullet)))))
+;;;_   > allout-encrypted-type-prefix (&optional prefix)
+(defun allout-encrypted-type-prefix (&optional prefix)
+  "True if current header prefix bullet is for an encrypted entry (body)."
+  (and allout-topic-encryption-bullet
+        (string= allout-topic-encryption-bullet
+                 (if prefix
+                     (allout-get-prefix-bullet prefix)
+                   (allout-get-bullet)))))
 ;;;_   > allout-bullet-for-depth (&optional depth)
 (defun allout-bullet-for-depth (&optional depth)
   "Return outline topic bullet suited to optional DEPTH, or current depth."
@@ -2510,8 +3506,8 @@ the current topics' depth.
 
 If SOLICIT is non-nil, then the choice of bullet is solicited from
 user.  If it's a character, then that character is offered as the
-default, otherwise the one suited to the context \(according to
-distinction or depth) is offered.  \(This overrides other options,
+default, otherwise the one suited to the context (according to
+distinction or depth) is offered.  (This overrides other options,
 including, eg, a distinctive PRIOR-BULLET.)  If non-nil, then the
 context-specific bullet is used.
 
@@ -2625,15 +3621,20 @@ index for each successive sibling)."
                                    ((allout-sibling-index))))))
     )
   )
-;;;_   > allout-open-topic (relative-depth &optional before use-sib-bullet)
-(defun allout-open-topic (relative-depth &optional before use-sib-bullet)
-  "Open a new topic at depth RELATIVE-DEPTH.
+;;;_   > allout-open-topic (relative-depth &optional before offer-recent-bullet)
+(defun allout-open-topic (relative-depth &optional before offer-recent-bullet)
+  "Open a new topic at depth DEPTH.
 
 New topic is situated after current one, unless optional flag BEFORE
-is non-nil, or unless current line is complete empty (not even
-whitespace), in which case open is done on current line.
+is non-nil, or unless current line is completely empty - lacking even
+whitespace - in which case open is done on the current line.
+
+When adding an offspring, it will be added immediately after the parent if
+the other offspring are exposed, or after the last child if the offspring
+are hidden.  (The intervening offspring will be exposed in the latter
+case.)
 
-If USE-SIB-BULLET is true, use the bullet of the prior sibling.
+If OFFER-RECENT-BULLET is true, offer to use the bullet of the prior sibling.
 
 Nuances:
 
@@ -2657,144 +3658,159 @@ Nuances:
   having to go to its preceding sibling, and then open forward
   from there."
 
-  (let* ((depth (+ (allout-current-depth) relative-depth))
-         (opening-on-blank (if (looking-at "^\$")
-                               (not (setq before nil))))
-         opening-numbered      ; Will get while computing ref-topic, below
-         ref-depth             ; Will get while computing ref-topic, below
-         ref-bullet            ; Will get while computing ref-topic, next
-         (ref-topic (save-excursion
-                      (cond ((< relative-depth 0)
-                             (allout-ascend-to-depth depth))
-                            ((>= relative-depth 1) nil)
-                            (t (allout-back-to-current-heading)))
-                      (setq ref-depth (allout-recent-depth))
-                      (setq ref-bullet
-                            (if (> allout-recent-prefix-end 1)
-                                (allout-recent-bullet)
-                              ""))
-                      (setq opening-numbered
-                            (save-excursion
-                              (and allout-numbered-bullet
-                                   (or (<= relative-depth 0)
-                                       (allout-descend-to-depth depth))
-                                   (if (allout-numbered-type-prefix)
-                                       allout-numbered-bullet))))
-                      (point)))
-         dbl-space
-         doing-beginning)
-
-    (if (not opening-on-blank)
+  (allout-beginning-of-current-line)
+  (save-match-data
+    (let* ((inhibit-field-text-motion t)
+           (depth (+ (allout-current-depth) relative-depth))
+           (opening-on-blank (if (looking-at "^\$")
+                                 (not (setq before nil))))
+           ;; bunch o vars set while computing ref-topic
+           opening-numbered
+           ref-depth
+           ref-bullet
+           (ref-topic (save-excursion
+                        (cond ((< relative-depth 0)
+                               (allout-ascend-to-depth depth))
+                              ((>= relative-depth 1) nil)
+                              (t (allout-back-to-current-heading)))
+                        (setq ref-depth allout-recent-depth)
+                        (setq ref-bullet
+                              (if (> allout-recent-prefix-end 1)
+                                  (allout-recent-bullet)
+                                ""))
+                        (setq opening-numbered
+                              (save-excursion
+                                (and allout-numbered-bullet
+                                     (or (<= relative-depth 0)
+                                         (allout-descend-to-depth depth))
+                                     (if (allout-numbered-type-prefix)
+                                         allout-numbered-bullet))))
+                        (point)))
+           dbl-space
+           doing-beginning
+           start end)
+
+      (if (not opening-on-blank)
                                         ; Positioning and vertical
                                         ; padding - only if not
                                         ; opening-on-blank:
-        (progn
-          (goto-char ref-topic)
-          (setq dbl-space               ; Determine double space action:
-                (or (and (<= relative-depth 0) ; not descending;
-                         (save-excursion
-                           ;; at b-o-b or preceded by a blank line?
-                           (or (> 0 (forward-line -1))
-                               (looking-at "^\\s-*$")
-                              (bobp)))
-                         (save-excursion
-                           ;; succeeded by a blank line?
-                           (allout-end-of-current-subtree)
-                           (bolp)))
-                    (and (= ref-depth 1)
-                         (or before
-                             (= depth 1)
-                             (save-excursion
-                               ;; Don't already have following
-                               ;; vertical padding:
-                               (not (allout-pre-next-preface)))))))
-
-                                        ; Position to prior heading,
-                                        ; if inserting backwards, and
-                                       ; not going outwards:
-          (if (and before (>= relative-depth 0))
-             (progn (allout-back-to-current-heading)
-                            (setq doing-beginning (bobp))
-                            (if (not (bobp))
-                                (allout-previous-heading)))
-           (if (and before (bobp))
-               (allout-unprotected (open-line 1))))
-
-          (if (<= relative-depth 0)
-              ;; Not going inwards, don't snug up:
-              (if doing-beginning
-                 (allout-unprotected (open-line (if dbl-space 2 1)))
-               (if before
-                   (progn (end-of-line)
-                          (allout-pre-next-preface)
-                          (while (= ?\r (following-char))
-                             (forward-char 1))
-                          (if (not (looking-at "^$"))
-                              (allout-unprotected (open-line 1))))
-                 (allout-end-of-current-subtree)))
-            ;; Going inwards - double-space if first offspring is,
-            ;; otherwise snug up.
-            (end-of-line)              ; So we skip any concealed progeny.
-            (allout-pre-next-preface)
-            (if (bolp)
-                ;; Blank lines between current header body and next
-                ;; header - get to last substantive (non-white-space)
-                ;; line in body:
-                (re-search-backward "[^ \t\n]" nil t))
-            (if (save-excursion
-                  (allout-next-heading)
-                  (if (> (allout-recent-depth) ref-depth)
+          (progn
+            (goto-char ref-topic)
+            (setq dbl-space             ; Determine double space action:
+                  (or (and (<= relative-depth 0) ; not descending;
+                           (save-excursion
+                             ;; at b-o-b or preceded by a blank line?
+                             (or (> 0 (forward-line -1))
+                                 (looking-at "^\\s-*$")
+                                 (bobp)))
+                           (save-excursion
+                             ;; succeeded by a blank line?
+                             (allout-end-of-current-subtree)
+                             (looking-at "\n\n")))
+                      (and (= ref-depth 1)
+                           (or before
+                               (= depth 1)
+                               (save-excursion
+                                 ;; Don't already have following
+                                 ;; vertical padding:
+                                 (not (allout-pre-next-prefix)))))))
+
+            ;; Position to prior heading, if inserting backwards, and not
+            ;; going outwards:
+            (if (and before (>= relative-depth 0))
+                (progn (allout-back-to-current-heading)
+                       (setq doing-beginning (bobp))
+                       (if (not (bobp))
+                           (allout-previous-heading)))
+              (if (and before (bobp))
+                  (open-line 1)))
+
+            (if (<= relative-depth 0)
+                ;; Not going inwards, don't snug up:
+                (if doing-beginning
+                    (if (not dbl-space)
+                        (open-line 1)
+                      (open-line 2))
+                  (if before
+                      (progn (end-of-line)
+                             (allout-pre-next-prefix)
+                             (while (and (= ?\n (following-char))
+                                         (save-excursion
+                                           (forward-char 1)
+                                           (allout-hidden-p)))
+                               (forward-char 1))
+                             (if (not (looking-at "^$"))
+                                 (open-line 1)))
+                    (allout-end-of-current-subtree)
+                    (if (looking-at "\n\n") (forward-char 1))))
+              ;; Going inwards - double-space if first offspring is
+              ;; double-spaced, otherwise snug up.
+              (allout-end-of-entry)
+              (if (eobp)
+                  (newline 1)
+                (line-move 1))
+              (allout-beginning-of-current-line)
+              (backward-char 1)
+              (if (bolp)
+                  ;; Blank lines between current header body and next
+                  ;; header - get to last substantive (non-white-space)
+                  ;; line in body:
+                  (progn (setq dbl-space t)
+                         (re-search-backward "[^ \t\n]" nil t)))
+              (if (looking-at "\n\n")
+                  (setq dbl-space t))
+              (if (save-excursion
+                    (allout-next-heading)
+                    (when (> allout-recent-depth ref-depth)
                       ;; This is an offspring.
-                      (progn (forward-line -1)
-                             (looking-at "^\\s-*$"))))
-                (progn (forward-line 1)
-                       (allout-unprotected (open-line 1))))
-            (end-of-line))
-          ;;(if doing-beginning (goto-char doing-beginning))
-          (if (not (bobp))
-              (progn (if (and (not (> depth ref-depth))
-                              (not before))
-                         (allout-unprotected (open-line 1))
-                      (if (> depth ref-depth)
-                          (allout-unprotected (newline 1))
-                        (if dbl-space
-                            (allout-unprotected (open-line 1))
-                          (if (not before)
-                              (allout-unprotected (newline 1))))))
-                     (if dbl-space
-                        (allout-unprotected (newline  1)))
-                     (if (and (not (eobp))
-                              (not (bolp)))
-                         (forward-char 1))))
-          ))
-    (insert (concat (allout-make-topic-prefix opening-numbered
-                                              t
-                                              depth)
-                   " "))
-
-    ;;(if doing-beginning (save-excursion (newline (if dbl-space 2 1))))
-
-
-    (allout-rebullet-heading (and use-sib-bullet ref-bullet);;; solicit
-                              depth                         ;;; depth
-                              nil                           ;;; number-control
-                              nil                           ;;; index
-                              t)     (end-of-line)
+                      (forward-line -1)
+                      (looking-at "^\\s-*$")))
+                  (progn (forward-line 1)
+                         (open-line 1)
+                         (forward-line 1)))
+              (allout-end-of-current-line))
+
+            ;;(if doing-beginning (goto-char doing-beginning))
+            (if (not (bobp))
+                ;; We insert a newline char rather than using open-line to
+                ;; avoid rear-stickiness inheritence of read-only property.
+                (progn (if (and (not (> depth ref-depth))
+                                (not before))
+                           (open-line 1)
+                         (if (and (not dbl-space) (> depth ref-depth))
+                             (newline 1)
+                           (if dbl-space
+                               (open-line 1)
+                             (if (not before)
+                                 (newline 1)))))
+                       (if (and dbl-space (not (> relative-depth 0)))
+                           (newline 1))
+                       (if (and (not (eobp))
+                                (or (not (bolp))
+                                    (and (not (bobp))
+                                         ;; bolp doesnt detect concealed
+                                         ;; trailing newlines, compensate:
+                                         (save-excursion
+                                           (forward-char -1)
+                                           (allout-hidden-p)))))
+                           (forward-char 1))))
+            ))
+      (setq start (point))
+      (insert (concat (allout-make-topic-prefix opening-numbered t depth)
+                      " "))
+      (setq end (1+ (point)))
+
+      (allout-rebullet-heading (and offer-recent-bullet ref-bullet)
+                               depth nil nil t)
+      (if (> relative-depth 0)
+          (save-excursion (goto-char ref-topic)
+                          (allout-show-children)))
+      (end-of-line)
+
+      (run-hook-with-args 'allout-structure-added-hook start end)
+      )
     )
   )
-;;;_    . open-topic contingencies
-;;;_     ; base topic - one from which open was issued
-;;;_      , beginning char
-;;;_      , amount of space before will be used, unless opening in place
-;;;_      , end char will be used, unless opening before (and it still may)
-;;;_     ; absolute depth of new topic
-;;;_     ! insert in place - overrides most stuff
-;;;_     ; relative depth of new re base
-;;;_     ; before or after base topic
-;;;_     ; spacing around topic, if any, prior to new topic and at same depth
-;;;_     ; buffer boundaries - special provisions for beginning and end ob
-;;;_     ; level 1 topics have special provisions also - double space.
-;;;_     ; location of new topic
 ;;;_   > allout-open-subtopic (arg)
 (defun allout-open-subtopic (arg)
   "Open new topic header at deeper level than the current one.
@@ -2802,7 +3818,7 @@ Nuances:
 Negative universal arg means to open deeper, but place the new topic
 prior to the current one."
   (interactive "p")
-  (allout-open-topic 1 (> 0 arg)))
+  (allout-open-topic 1 (> 0 arg) (< 1 arg)))
 ;;;_   > allout-open-sibtopic (arg)
 (defun allout-open-sibtopic (arg)
   "Open new topic header at same level as the current one.
@@ -2812,7 +3828,7 @@ Positive universal arg means to use the bullet of the prior sibling.
 Negative universal arg means to place the new topic prior to the current
 one."
   (interactive "p")
-  (allout-open-topic 0 (> 0 arg) (< 1 arg)))
+  (allout-open-topic 0 (> 0 arg) (not (= 1 arg))))
 ;;;_   > allout-open-supertopic (arg)
 (defun allout-open-supertopic (arg)
   "Open new topic header at shallower level than the current one.
@@ -2821,7 +3837,7 @@ Negative universal arg means to open shallower, but place the new
 topic prior to the current one."
 
   (interactive "p")
-  (allout-open-topic -1 (> 0 arg)))
+  (allout-open-topic -1 (> 0 arg) (< 1 arg)))
 
 ;;;_  - Outline Alteration
 ;;;_   : Topic Modification
@@ -2834,18 +3850,24 @@ topic prior to the current one."
 
 Maintains outline hanging topic indentation if
 `allout-use-hanging-indents' is set."
-  (let ((fill-prefix (if allout-use-hanging-indents
-                         ;; Check for topic header indentation:
-                         (save-excursion
-                           (beginning-of-line)
-                           (if (looking-at allout-regexp)
-                               ;; ... construct indentation to account for
-                               ;; length of topic prefix:
-                               (make-string (progn (allout-end-of-prefix)
-                                                   (current-column))
-                                            ?\ ))))))
-    (if (or allout-former-auto-filler allout-use-hanging-indents)
-        (do-auto-fill))))
+
+  (when (not allout-inhibit-auto-fill)
+    (let ((fill-prefix (if allout-use-hanging-indents
+                           ;; Check for topic header indentation:
+                           (save-match-data
+                             (save-excursion
+                               (beginning-of-line)
+                               (if (looking-at allout-regexp)
+                                   ;; ... construct indentation to account for
+                                   ;; length of topic prefix:
+                                   (make-string (progn (allout-end-of-prefix)
+                                                       (current-column))
+                                                ?\ ))))))
+          (use-auto-fill-function (or allout-outside-normal-auto-fill-function
+                                      auto-fill-function
+                                      'do-auto-fill)))
+      (if (or allout-former-auto-filler allout-use-hanging-indents)
+          (funcall use-auto-fill-function)))))
 ;;;_    > allout-reindent-body (old-depth new-depth &optional number)
 (defun allout-reindent-body (old-depth new-depth &optional number)
   "Reindent body lines which were indented at OLD-DEPTH to NEW-DEPTH.
@@ -2859,7 +3881,6 @@ Note that refill of indented paragraphs is not done."
     (allout-end-of-prefix)
     (let* ((new-margin (current-column))
           excess old-indent-begin old-indent-end
-          curr-ind
           ;; We want the column where the header-prefix text started
           ;; *before* the prefix was changed, so we infer it relative
           ;; to the new margin and the shift in depth:
@@ -2869,7 +3890,7 @@ Note that refill of indented paragraphs is not done."
       (allout-unprotected
        (save-match-data
          (while
-            (and (re-search-forward "[\n\r]\\(\\s-*\\)"
+            (and (re-search-forward "\n\\(\\s-*\\)"
                                     nil
                                     t)
                  ;; Register the indent data, before we reset the
@@ -2877,38 +3898,43 @@ Note that refill of indented paragraphs is not done."
                  (setq old-indent-begin (match-beginning 1)
                        old-indent-end (match-end 1))
                  (not (looking-at allout-regexp)))
-          (if (> 0 (setq excess (- (current-column)
-                                    old-margin)))
+          (if (> 0 (setq excess (- (- old-indent-end old-indent-begin)
+                                    old-margin)))
               ;; Text starts left of old margin - don't adjust:
               nil
             ;; Text was hanging at or right of old left margin -
             ;; reindent it, preserving its existing indentation
             ;; beyond the old margin:
             (delete-region old-indent-begin old-indent-end)
-            (indent-to (+ new-margin excess)))))))))
+             (indent-to (+ new-margin excess (current-column))))))))))
 ;;;_    > allout-rebullet-current-heading (arg)
 (defun allout-rebullet-current-heading (arg)
   "Solicit new bullet for current visible heading."
   (interactive "p")
   (let ((initial-col (current-column))
        (on-bullet (eq (point)(allout-current-bullet-pos)))
+        from to
        (backwards (if (< arg 0)
                       (setq arg (* arg -1)))))
     (while (> arg 0)
       (save-excursion (allout-back-to-current-heading)
                      (allout-end-of-prefix)
+                      (setq from allout-recent-prefix-beginning
+                            to allout-recent-prefix-end)
                      (allout-rebullet-heading t        ;;; solicit
                                                nil     ;;; depth
                                                nil     ;;; number-control
                                                nil     ;;; index
-                                               t))     ;;; do-successors
+                                               t)      ;;; do-successors
+                      (run-hook-with-args 'allout-exposure-change-hook
+                                          from to t))
       (setq arg (1- arg))
       (if (<= arg 0)
          nil
        (setq initial-col nil)          ; Override positioning back to init col
        (if (not backwards)
            (allout-next-visible-heading 1)
-         (allout-goto-prefix)
+         (allout-goto-prefix-doublechecked)
          (allout-next-visible-heading -1))))
     (message "Done.")
     (cond (on-bullet (goto-char (allout-current-bullet-pos)))
@@ -2922,28 +3948,30 @@ Note that refill of indented paragraphs is not done."
 
   "Adjust bullet of current topic prefix.
 
+All args are optional.
+
 If SOLICIT is non-nil, then the choice of bullet is solicited from
 user.  If it's a character, then that character is offered as the
-default, otherwise the one suited to the context \(according to
+default, otherwise the one suited to the context (according to
 distinction or depth) is offered.  If non-nil, then the
 context-specific bullet is just used.
 
-Second arg NEW-DEPTH forces the topic prefix to that depth, regardless
+Second arg DEPTH forces the topic prefix to that depth, regardless
 of the topic's current depth.
 
 Third arg NUMBER-CONTROL can force the prefix to or away from
 numbered form.  It has effect only if `allout-numbered-bullet' is
 non-nil and soliciting was not explicitly invoked (via first arg).
 Its effect, numbering or denumbering, then depends on the setting
-of the fourth arg, INDEX.
+of the forth arg, INDEX.
 
-If NUMBER-CONTROL is non-nil and fourth arg INDEX is nil, then the
+If NUMBER-CONTROL is non-nil and forth arg INDEX is nil, then the
 prefix of the topic is forced to be non-numbered.  Null index and
 non-nil NUMBER-CONTROL forces denumbering.  Non-nil INDEX (and
 non-nil NUMBER-CONTROL) forces a numbered-prefix form.  If non-nil
 INDEX is a number, then that number is used for the numbered
 prefix.  Non-nil and non-number means that the index for the
-numbered prefix will be derived by `allout-make-topic-prefix'.
+numbered prefix will be derived by allout-make-topic-prefix.
 
 Fifth arg DO-SUCCESSORS t means re-resolve count on succeeding
 siblings.
@@ -2956,7 +3984,8 @@ this function."
          (new-depth (or new-depth current-depth))
          (mb allout-recent-prefix-beginning)
          (me allout-recent-prefix-end)
-         (current-bullet (buffer-substring (- me 1) me))
+         (current-bullet (buffer-substring-no-properties (- me 1) me))
+         (has-annotation (get-text-property mb 'allout-was-hidden))
          (new-prefix (allout-make-topic-prefix current-bullet
                                                 nil
                                                 new-depth
@@ -2977,18 +4006,25 @@ this function."
       (goto-char mb)
                                        ; Dispense with number if
                                        ; numbered-bullet prefix:
-      (if (and allout-numbered-bullet
-               (string= allout-numbered-bullet current-bullet)
-               (looking-at "[0-9]+"))
-         (allout-unprotected
-          (delete-region (match-beginning 0)(match-end 0))))
+      (save-match-data
+        (if (and allout-numbered-bullet
+                 (string= allout-numbered-bullet current-bullet)
+                 (looking-at "[0-9]+"))
+            (allout-unprotected
+             (delete-region (match-beginning 0)(match-end 0)))))
+
+      ;; convey 'allout-was-hidden annotation, if original had it:
+      (if has-annotation
+          (put-text-property 0 (length new-prefix) 'allout-was-hidden t
+                             new-prefix))
 
                                        ; Put in new prefix:
       (allout-unprotected (insert new-prefix))
 
-      ;; Reindent the body if elected and margin changed:
+      ;; Reindent the body if elected, margin changed, and not encrypted body:
       (if (and allout-reindent-bodies
-              (not (= new-depth current-depth)))
+              (not (= new-depth current-depth))
+               (not (allout-encrypted-topic-p)))
          (allout-reindent-body current-depth new-depth))
 
       ;; Recursively rectify successive siblings of orig topic if
@@ -3009,15 +4045,20 @@ this function."
     ) ; let* ((current-depth (allout-depth))...)
   ) ; defun
 ;;;_    > allout-rebullet-topic (arg)
-(defun allout-rebullet-topic (arg)
-  "Like `allout-rebullet-topic-grunt', but start from topic visible at point.
+(defun allout-rebullet-topic (arg &optional sans-offspring)
+  "Rebullet the visible topic containing point and all contained subtopics.
 
 Descends into invisible as well as visible topics, however.
 
+When optional sans-offspring is non-nil, subtopics are not
+shifted.  (Shifting a topic outwards without shifting its
+offspring is disallowed, since this would create a \"containment
+discontinuity\", where the depth difference between a topic and
+its immediate offspring is greater than one.)
+
 With repeat count, shift topic depth by that amount."
   (interactive "P")
-  (let ((start-col (current-column))
-        (was-eol (eolp)))
+  (let ((start-col (current-column)))
     (save-excursion
       ;; Normalize arg:
       (cond ((null arg) (setq arg 0))
@@ -3025,29 +4066,45 @@ With repeat count, shift topic depth by that amount."
       ;; Fill the user in, in case we're shifting a big topic:
       (if (not (zerop arg)) (message "Shifting..."))
       (allout-back-to-current-heading)
-      (if (<= (+ (allout-recent-depth) arg) 0)
+      (if (<= (+ allout-recent-depth arg) 0)
           (error "Attempt to shift topic below level 1"))
-      (allout-rebullet-topic-grunt arg)
+      (allout-rebullet-topic-grunt arg nil nil nil nil sans-offspring)
       (if (not (zerop arg)) (message "Shifting... done.")))
     (move-to-column (max 0 (+ start-col arg)))))
-;;;_     > allout-rebullet-topic-grunt (&optional relative-depth ...)
+;;;_    > allout-rebullet-topic-grunt (&optional relative-depth ...)
 (defun allout-rebullet-topic-grunt (&optional relative-depth
                                                starting-depth
                                                starting-point
                                                index
-                                               do-successors)
+                                               do-successors
+                                               sans-offspring)
+  "Like `allout-rebullet-topic', but on nearest containing topic
+\(visible or not).
+
+See `allout-rebullet-heading' for rebulleting behavior.
 
-  "Rebullet the topic at point, visible or invisible, and all
-contained subtopics.  See `allout-rebullet-heading' for rebulleting
-behavior.
+All arguments are optional.
 
-Arg RELATIVE-DEPTH means to shift the depth of the entire
+First arg RELATIVE-DEPTH means to shift the depth of the entire
 topic that amount.
 
-\(fn &optional RELATIVE-DEPTH)"
+Several subsequent args are for internal recursive use by the function
+itself: STARTING-DEPTH, STARTING-POINT, and INDEX.
+
+Finally, if optional SANS-OFFSPRING is non-nil then the offspring
+are not shifted.  (Shifting a topic outwards without shifting
+its offspring is disallowed, since this would create a
+\"containment discontinuity\", where the depth difference between
+a topic and its immediate offspring is greater than one..)"
+
+  ;; XXX the recursion here is peculiar, and in general the routine may
+  ;; need simplification with refactoring.
 
-  ;; All args except the first one are for internal recursive use by the
-  ;; function itself.
+  (if (and sans-offspring
+           relative-depth
+           (< relative-depth 0))
+      (error (concat "Attempt to shift topic outwards without offspring,"
+                     " would cause containment discontinuity.")))
 
   (let* ((relative-depth (or relative-depth 0))
          (new-depth (allout-depth))
@@ -3059,44 +4116,57 @@ topic that amount.
                     (and (or (zerop relative-depth)
                              (not on-starting-call))
                          (allout-sibling-index))))
+         (starting-index index)
          (moving-outwards (< 0 relative-depth))
-         (starting-point (or starting-point (point))))
+         (starting-point (or starting-point (point)))
+         (local-point (point)))
 
     ;; Sanity check for excessive promotion done only on starting call:
     (and on-starting-call
          moving-outwards
          (> 0 (+ starting-depth relative-depth))
-         (error "Attempt to shift topic out beyond level 1"))  ;;; ====>
+         (error "Attempt to shift topic out beyond level 1"))
 
     (cond ((= starting-depth new-depth)
-           ;; We're at depth to work on this one:
-           (allout-rebullet-heading nil                ;;; solicit
-                                     (+ starting-depth ;;; starting-depth
-                                        relative-depth)
-                                     nil               ;;; number
-                                     index             ;;; index
-                                     ;; Every contained topic will get hit,
-                                     ;; and we have to get to outside ones
-                                     ;; deliberately:
-                                     nil)              ;;; do-successors
-           ;; ... and work on subsequent ones which are at greater depth:
-           (setq index 0)
-           (allout-next-heading)
-           (while (and (not (eobp))
-                       (< starting-depth (allout-recent-depth)))
-             (setq index (1+ index))
-             (allout-rebullet-topic-grunt relative-depth   ;;; relative-depth
-                                           (1+ starting-depth);;;starting-depth
-                                           starting-point   ;;; starting-point
-                                           index)))        ;;; index
+           ;; We're at depth to work on this one.
+
+           ;; When shifting out we work on the children before working on
+           ;; the parent to avoid interim `allout-aberrant-container-p'
+           ;; aberrancy, and vice-versa when shifting in:
+           (if (>= relative-depth 0)
+               (allout-rebullet-heading nil
+                                        (+ starting-depth relative-depth)
+                                        nil            ;;; number
+                                        index
+                                        nil)) ;;; do-successors
+           (when (not sans-offspring)
+             ;; ... and work on subsequent ones which are at greater depth:
+             (setq index 0)
+             (allout-next-heading)
+             (while (and (not (eobp))
+                         (< starting-depth (allout-depth)))
+               (setq index (1+ index))
+               (allout-rebullet-topic-grunt relative-depth
+                                            (1+ starting-depth)
+                                            starting-point
+                                            index)))
+           (when (< relative-depth 0)
+             (save-excursion
+               (goto-char local-point)
+               (allout-rebullet-heading nil               ;;; solicit
+                                        (+ starting-depth relative-depth)
+                                        nil            ;;; number
+                                        starting-index
+                                        nil)))) ;;; do-successors
 
           ((< starting-depth new-depth)
            ;; Rare case - subtopic more than one level deeper than parent.
            ;; Treat this one at an even deeper level:
-           (allout-rebullet-topic-grunt relative-depth   ;;; relative-depth
-                                         new-depth       ;;; starting-depth
-                                         starting-point          ;;; starting-point
-                                         index)))        ;;; index
+           (allout-rebullet-topic-grunt relative-depth
+                                         new-depth
+                                         starting-point
+                                         index
+                                         sans-offspring)))
 
     (if on-starting-call
         (progn
@@ -3104,8 +4174,8 @@ topic that amount.
           ;; if topic has changed depth
           (if (or do-successors
                   (and (not (zerop relative-depth))
-                       (or (= (allout-recent-depth) starting-depth)
-                           (= (allout-recent-depth) (+ starting-depth
+                       (or (= allout-recent-depth starting-depth)
+                           (= allout-recent-depth (+ starting-depth
                                                         relative-depth)))))
               (allout-rebullet-heading nil nil nil nil t))
           ;; Now rectify numbering of new siblings of the adjusted topic,
@@ -3130,24 +4200,24 @@ Returns final depth."
        was-eobp)
     (while (and (not (eobp))
                (allout-depth)
-                (>= (allout-recent-depth) depth)
+                (>= allout-recent-depth depth)
                 (>= ascender depth))
                                         ; Skip over all topics at
                                         ; lesser depths, which can not
                                         ; have been disturbed:
       (while (and (not (setq was-eobp (eobp)))
-                 (> (allout-recent-depth) ascender))
+                 (> allout-recent-depth ascender))
         (allout-next-heading))
                                         ; Prime ascender for ascension:
-      (setq ascender (1- (allout-recent-depth)))
-      (if (>= (allout-recent-depth) depth)
+      (setq ascender (1- allout-recent-depth))
+      (if (>= allout-recent-depth depth)
           (allout-rebullet-heading nil ;;; solicit
                                     nil        ;;; depth
                                     nil        ;;; number-control
                                     nil        ;;; index
                                     t)) ;;; do-successors
       (if was-eobp (goto-char (point-max)))))
-  (allout-recent-depth))
+  allout-recent-depth)
 ;;;_    > allout-number-siblings (&optional denumber)
 (defun allout-number-siblings (&optional denumber)
   "Assign numbered topic prefix to this topic and its siblings.
@@ -3163,7 +4233,7 @@ rebulleting each topic at this level."
   (save-excursion
     (allout-back-to-current-heading)
     (allout-beginning-of-level)
-    (let ((depth (allout-recent-depth))
+    (let ((depth allout-recent-depth)
          (index (if (not denumber) 1))
           (use-bullet (equal '(16) denumber))
           (more t))
@@ -3177,73 +4247,258 @@ rebulleting each topic at this level."
         (setq more (allout-next-sibling depth nil))))))
 ;;;_    > allout-shift-in (arg)
 (defun allout-shift-in (arg)
-  "Increase depth of current heading and any topics collapsed within it."
+  "Increase depth of current heading and any items collapsed within it.
+
+With a negative argument, the item is shifted out using
+`allout-shift-out', instead.
+
+With an argument greater than one, shift-in the item but not its
+offspring, making the item into a sibling of its former children,
+and a child of sibling that formerly preceeded it.
+
+You are not allowed to shift the first offspring of a topic
+inwards, because that would yield a \"containment
+discontinuity\", where the depth difference between a topic and
+its immediate offspring is greater than one.  The first topic in
+the file can be adjusted to any positive depth, however."
+
   (interactive "p")
-  (allout-rebullet-topic arg))
+  (if (< arg 0)
+      (allout-shift-out (* arg -1))
+    ;; refuse to create a containment discontinuity:
+    (save-excursion
+      (allout-back-to-current-heading)
+      (if (not (bobp))
+          (let* ((current-depth allout-recent-depth)
+                 (start-point (point))
+                 (predecessor-depth (progn
+                                      (forward-char -1)
+                                      (allout-goto-prefix-doublechecked)
+                                      (if (< (point) start-point)
+                                          allout-recent-depth
+                                        0))))
+            (if (and (> predecessor-depth 0)
+                     (> (1+ current-depth)
+                        (1+ predecessor-depth)))
+                (error (concat "Disallowed shift deeper than"
+                               " containing topic's children."))
+              (allout-back-to-current-heading)
+              (if (< allout-recent-depth (1+ current-depth))
+                  (allout-show-children))))))
+    (let ((where (point)))
+      (allout-rebullet-topic 1 (and (> arg 1) 'sans-offspring))
+      (run-hook-with-args 'allout-structure-shifted-hook arg where))))
 ;;;_    > allout-shift-out (arg)
 (defun allout-shift-out (arg)
-  "Decrease depth of current heading and any topics collapsed within it."
+  "Decrease depth of current heading and any topics collapsed within it.
+This will make the item a sibling of its former container.
+
+With a negative argument, the item is shifted in using
+`allout-shift-in', instead.
+
+With an argument greater than one, shift-out the item's offspring
+but not the item itself, making the former children siblings of
+the item.
+
+With an argument greater than 1, the item's offspring are shifted
+out without shifting the item.  This will make the immediate
+subtopics into siblings of the item."
   (interactive "p")
-  (allout-rebullet-topic (* arg -1)))
+  (if (< arg 0)
+      (allout-shift-in (* arg -1))
+    ;; Get proper exposure in this area:
+    (save-excursion (if (allout-ascend)
+                        (allout-show-children)))
+    ;; Show collapsed children if there's a successor which will become
+    ;; their sibling:
+    (if (and (allout-current-topic-collapsed-p)
+             (save-excursion (allout-next-sibling)))
+        (allout-show-children))
+    (let ((where (and (allout-depth) allout-recent-prefix-beginning)))
+      (save-excursion
+        (if (> arg 1)
+            ;; Shift the offspring but not the topic:
+            (let ((children-chart (allout-chart-subtree 1)))
+              (if (listp (car children-chart))
+                  ;; whoops:
+                  (setq children-chart (allout-flatten children-chart)))
+              (save-excursion
+                (dolist (child-point children-chart)
+                  (goto-char child-point)
+                  (allout-shift-out 1))))
+          (allout-rebullet-topic (* arg -1))))
+      (run-hook-with-args 'allout-structure-shifted-hook (* arg -1) where))))
 ;;;_   : Surgery (kill-ring) functions with special provisions for outlines:
 ;;;_    > allout-kill-line (&optional arg)
 (defun allout-kill-line (&optional arg)
   "Kill line, adjusting subsequent lines suitably for outline mode."
 
   (interactive "*P")
-  (if (not (and (allout-mode-p)                ; active outline mode,
-               allout-numbered-bullet          ; numbers may need adjustment,
-               (bolp)                          ; may be clipping topic head,
-               (looking-at allout-regexp)))    ; are clipping topic head.
-      ;; Above conditions do not obtain - just do a regular kill:
+
+  (if (or (not (allout-mode-p))
+          (not (bolp))
+          (not (save-match-data (looking-at allout-regexp))))
+      ;; Just do a regular kill:
       (kill-line arg)
     ;; Ah, have to watch out for adjustments:
-    (let* ((depth (allout-depth)))
-                                        ; Do the kill:
-      (kill-line arg)
-                                        ; Provide some feedback:
-      (sit-for 0)
-      (save-excursion
-                                        ; Start with the topic
-                                        ; following killed line:
-        (if (not (looking-at allout-regexp))
-            (allout-next-heading))
-        (allout-renumber-to-depth depth)))))
+    (let* ((beg (point))
+           end
+           (beg-hidden (allout-hidden-p))
+           (end-hidden (save-excursion (allout-end-of-current-line)
+                                       (setq end (point))
+                                       (allout-hidden-p)))
+           (depth (allout-depth)))
+
+      (allout-annotate-hidden beg end)
+      (if (and (not beg-hidden) (not end-hidden))
+          (allout-unprotected (kill-line arg))
+        (kill-line arg))
+      (allout-deannotate-hidden beg end)
+
+      (if allout-numbered-bullet
+          (save-excursion               ; Renumber subsequent topics if needed:
+            (if (not (save-match-data (looking-at allout-regexp)))
+                (allout-next-heading))
+            (allout-renumber-to-depth depth)))
+      (run-hook-with-args 'allout-structure-deleted-hook depth (point)))))
+;;;_    > allout-copy-line-as-kill ()
+(defun allout-copy-line-as-kill ()
+  "Like allout-kill-topic, but save to kill ring instead of deleting."
+  (interactive)
+  (let ((buffer-read-only t))
+    (condition-case nil
+        (allout-kill-line)
+      (buffer-read-only nil))))
 ;;;_    > allout-kill-topic ()
 (defun allout-kill-topic ()
   "Kill topic together with subtopics.
 
-Leaves primary topic's trailing vertical whitespace, if any."
+Trailing whitespace is killed with a topic if that whitespace:
 
-  ;; Some finagling is done to make complex topic kills appear faster
-  ;; than they actually are.  A redisplay is performed immediately
-  ;; after the region is disposed of, though the renumbering process
-  ;; has yet to be performed.  This means that there may appear to be
-  ;; a lag *after* the kill has been performed.
+ - would separate the topic from a subsequent sibling
+ - would separate the topic from the end of buffer
+ - would not be added to whitespace already separating the topic from the
+   previous one.
+
+Topic exposure is marked with text-properties, to be used by
+allout-yank-processing for exposure recovery."
 
   (interactive)
-  (let* ((beg (prog1 (allout-back-to-current-heading)(beginning-of-line)))
-         (depth (allout-recent-depth)))
+  (let* ((inhibit-field-text-motion t)
+         (beg (prog1 (allout-back-to-current-heading) (beginning-of-line)))
+         end
+         (depth allout-recent-depth))
     (allout-end-of-current-subtree)
+    (if (and (/= (current-column) 0) (not (eobp)))
+        (forward-char 1))
     (if (not (eobp))
-       (if (or (not (looking-at "^$"))
-               ;; A blank line - cut it with this topic *unless* this
-               ;; is the last topic at this level, in which case
-               ;; we'll leave the blank line as part of the
-               ;; containing topic:
-               (save-excursion
-                 (and (allout-next-heading)
-                      (>= (allout-recent-depth) depth))))
+       (if (and (save-match-data (looking-at "\n"))
+                 (or (save-excursion
+                       (or (not (allout-next-heading))
+                           (= depth allout-recent-depth)))
+                     (and (> (- beg (point-min)) 3)
+                          (string= (buffer-substring (- beg 2) beg) "\n\n"))))
            (forward-char 1)))
 
-    (kill-region beg (point))
-    (sit-for 0)
+    (allout-annotate-hidden beg (setq end (point)))
+    (unwind-protect
+        (allout-unprotected (kill-region beg end))
+      (if buffer-read-only
+          ;; eg, during copy-as-kill.
+          (allout-deannotate-hidden beg end)))
+
     (save-excursion
-      (allout-renumber-to-depth depth))))
+      (allout-renumber-to-depth depth))
+    (run-hook-with-args 'allout-structure-deleted-hook depth (point))))
+;;;_    > allout-copy-topic-as-kill ()
+(defun allout-copy-topic-as-kill ()
+  "Like allout-kill-topic, but save to kill ring instead of deleting."
+  (interactive)
+  (let ((buffer-read-only t))
+    (condition-case nil
+        (allout-kill-topic)
+      (buffer-read-only (message "Topic copied...")))))
+;;;_    > allout-annotate-hidden (begin end)
+(defun allout-annotate-hidden (begin end)
+  "Qualify text with properties to indicate exposure status."
+
+  (let ((was-modified (buffer-modified-p))
+        (buffer-read-only nil))
+    (allout-deannotate-hidden begin end)
+    (save-excursion
+      (goto-char begin)
+      (let (done next prev overlay)
+        (while (not done)
+          ;; at or advance to start of next hidden region:
+          (if (not (allout-hidden-p))
+              (setq next
+                    (max (1+ (point))
+                         (next-single-char-property-change (point)
+                                                           'invisible
+                                                           nil end))))
+          (if (or (not next) (eq prev next))
+              ;; still not at start of hidden area - must not be any left.
+              (setq done t)
+            (goto-char next)
+            (setq prev next)
+            (if (not (allout-hidden-p))
+                ;; still not at start of hidden area.
+                (setq done t)
+              (setq overlay (allout-get-invisibility-overlay))
+              (setq next (overlay-end overlay)
+                    prev next)
+              ;; advance to end of this hidden area:
+              (when next
+                (goto-char next)
+                (allout-unprotected
+                 (let ((buffer-undo-list t))
+                   (put-text-property (overlay-start overlay) next
+                                      'allout-was-hidden t)))))))))
+    (set-buffer-modified-p was-modified)))
+;;;_    > allout-deannotate-hidden (begin end)
+(defun allout-deannotate-hidden (begin end)
+  "Remove allout hidden-text annotation between BEGIN and END."
+
+  (allout-unprotected
+   (let ((inhibit-read-only t)
+         (buffer-undo-list t))
+     ;(remove-text-properties begin end '(allout-was-hidden t))
+     )))
+;;;_    > allout-hide-by-annotation (begin end)
+(defun allout-hide-by-annotation (begin end)
+  "Translate text properties indicating exposure status into actual exposure."
+  (save-excursion
+    (goto-char begin)
+    (let ((was-modified (buffer-modified-p))
+          done next prev)
+      (while (not done)
+        ;; at or advance to start of next annotation:
+        (if (not (get-text-property (point) 'allout-was-hidden))
+            (setq next (next-single-char-property-change (point)
+                                                         'allout-was-hidden
+                                                         nil end)))
+        (if (or (not next) (eq prev next))
+            ;; no more or not advancing - must not be any left.
+            (setq done t)
+          (goto-char next)
+          (setq prev next)
+          (if (not (get-text-property (point) 'allout-was-hidden))
+              ;; still not at start of annotation.
+              (setq done t)
+            ;; advance to just after end of this annotation:
+            (setq next (next-single-char-property-change (point)
+                                                         'allout-was-hidden
+                                                         nil end))
+            (overlay-put (make-overlay prev next nil 'front-advance)
+                         'category 'allout-exposure-category)
+            (allout-deannotate-hidden prev next)
+            (setq prev next)
+            (if next (goto-char next)))))
+      (set-buffer-modified-p was-modified))))
 ;;;_    > allout-yank-processing ()
 (defun allout-yank-processing (&optional arg)
 
-  "Incidental outline specific business to be done just after text yanks.
+  "Incidental allout-specific business to be done just after text yanks.
 
 Does depth adjustment of yanked topics, when:
 
@@ -3259,114 +4514,127 @@ header into which it's being yanked.
 
 The point is left in front of yanked, adjusted topics, rather than
 at the end (and vice-versa with the mark).  Non-adjusted yanks,
-however, are left exactly like normal, not outline specific yanks."
+however, are left exactly like normal, non-allout-specific yanks."
 
   (interactive "*P")
                                        ; Get to beginning, leaving
                                        ; region around subject:
-  (if (< (my-mark-marker t) (point))
+  (if (< (allout-mark-marker t) (point))
       (exchange-point-and-mark))
-  (let* ((subj-beg (point))
-        (subj-end (my-mark-marker t))
-        ;; 'resituate' if yanking an entire topic into topic header:
-        (resituate (and (allout-e-o-prefix-p)
-                        (looking-at (concat "\\(" allout-regexp "\\)"))
-                        (allout-prefix-data (match-beginning 1)
-                                             (match-end 1))))
-        ;; `rectify-numbering' if resituating (where several topics may
-        ;; be resituating) or yanking a topic into a topic slot (bol):
-        (rectify-numbering (or resituate
-                               (and (bolp) (looking-at allout-regexp)))))
-    (if resituate
-                                        ; The yanked stuff is a topic:
-       (let* ((prefix-len (- (match-end 1) subj-beg))
-              (subj-depth (allout-recent-depth))
-              (prefix-bullet (allout-recent-bullet))
-              (adjust-to-depth
-               ;; Nil if adjustment unnecessary, otherwise depth to which
-               ;; adjustment should be made:
-               (save-excursion
-                 (and (goto-char subj-end)
-                      (eolp)
-                      (goto-char subj-beg)
-                      (and (looking-at allout-regexp)
-                           (progn
-                             (beginning-of-line)
-                             (not (= (point) subj-beg)))
-                           (looking-at allout-regexp)
-                           (allout-prefix-data (match-beginning 0)
-                                                (match-end 0)))
-                      (allout-recent-depth))))
-              done
-              (more t))
-         (setq rectify-numbering allout-numbered-bullet)
-         (if adjust-to-depth
+  (save-match-data
+    (let* ((subj-beg (point))
+           (into-bol (bolp))
+           (subj-end (allout-mark-marker t))
+           ;; 'resituate' if yanking an entire topic into topic header:
+           (resituate (and (let ((allout-inhibit-aberrance-doublecheck t))
+                             (allout-e-o-prefix-p))
+                           (looking-at allout-regexp)
+                           (allout-prefix-data)))
+           ;; `rectify-numbering' if resituating (where several topics may
+           ;; be resituating) or yanking a topic into a topic slot (bol):
+           (rectify-numbering (or resituate
+                                  (and into-bol
+                                       (looking-at allout-regexp)))))
+      (if resituate
+          ;; Yanking a topic into the start of a topic - reconcile to fit:
+          (let* ((inhibit-field-text-motion t)
+                 (prefix-len (if (not (match-end 1))
+                                 1
+                               (- (match-end 1) subj-beg)))
+                 (subj-depth allout-recent-depth)
+                 (prefix-bullet (allout-recent-bullet))
+                 (adjust-to-depth
+                  ;; Nil if adjustment unnecessary, otherwise depth to which
+                  ;; adjustment should be made:
+                  (save-excursion
+                    (and (goto-char subj-end)
+                         (eolp)
+                         (goto-char subj-beg)
+                         (and (looking-at allout-regexp)
+                              (progn
+                                (beginning-of-line)
+                                (not (= (point) subj-beg)))
+                              (looking-at allout-regexp)
+                              (allout-prefix-data))
+                         allout-recent-depth)))
+                 (more t))
+            (setq rectify-numbering allout-numbered-bullet)
+            (if adjust-to-depth
                                         ; Do the adjustment:
-             (progn
-               (message "... yanking") (sit-for 0)
-               (save-restriction
-                 (narrow-to-region subj-beg subj-end)
+                (progn
+                  (save-restriction
+                    (narrow-to-region subj-beg subj-end)
                                         ; Trim off excessive blank
                                         ; line at end, if any:
-                 (goto-char (point-max))
-                 (if (looking-at "^$")
-                     (allout-unprotected (delete-char -1)))
+                    (goto-char (point-max))
+                    (if (looking-at "^$")
+                        (allout-unprotected (delete-char -1)))
                                         ; Work backwards, with each
                                         ; shallowest level,
                                         ; successively excluding the
                                         ; last processed topic from
                                         ; the narrow region:
-                 (while more
-                   (allout-back-to-current-heading)
+                    (while more
+                      (allout-back-to-current-heading)
                                         ; go as high as we can in each bunch:
-                   (while (allout-ascend-to-depth (1- (allout-depth))))
-                   (save-excursion
-                     (allout-rebullet-topic-grunt (- adjust-to-depth
-                                                      subj-depth))
-                     (allout-depth))
-                   (if (setq more (not (bobp)))
-                       (progn (widen)
-                              (forward-char -1)
-                              (narrow-to-region subj-beg (point))))))
-               (message "")
-               ;; Preserve new bullet if it's a distinctive one, otherwise
-               ;; use old one:
-               (if (string-match (regexp-quote prefix-bullet)
-                                 allout-distinctive-bullets-string)
+                      (while (allout-ascend t))
+                      (save-excursion
+                        (allout-unprotected
+                         (allout-rebullet-topic-grunt (- adjust-to-depth
+                                                         subj-depth)))
+                        (allout-depth))
+                      (if (setq more (not (bobp)))
+                          (progn (widen)
+                                 (forward-char -1)
+                                 (narrow-to-region subj-beg (point))))))
+                  ;; Preserve new bullet if it's a distinctive one, otherwise
+                  ;; use old one:
+                  (if (string-match (regexp-quote prefix-bullet)
+                                    allout-distinctive-bullets-string)
                                         ; Delete from bullet of old to
                                         ; before bullet of new:
-                   (progn
-                     (beginning-of-line)
-                     (delete-region (point) subj-beg)
-                     (set-marker (my-mark-marker t) subj-end)
-                     (goto-char subj-beg)
-                     (allout-end-of-prefix))
+                      (progn
+                        (beginning-of-line)
+                        (allout-unprotected
+                         (delete-region (point) subj-beg))
+                        (set-marker (allout-mark-marker t) subj-end)
+                        (goto-char subj-beg)
+                        (allout-end-of-prefix))
                                         ; Delete base subj prefix,
                                         ; leaving old one:
-                 (delete-region (point) (+ (point)
-                                           prefix-len
-                                           (- adjust-to-depth subj-depth)))
+                    (allout-unprotected
+                     (progn
+                       (delete-region (point) (+ (point)
+                                                 prefix-len
+                                                 (- adjust-to-depth
+                                                    subj-depth)))
                                         ; and delete residual subj
                                         ; prefix digits and space:
-                 (while (looking-at "[0-9]") (delete-char 1))
-                 (if (looking-at " ") (delete-char 1))))
-           (exchange-point-and-mark))))
-    (if rectify-numbering
-       (progn
-         (save-excursion
+                       (while (looking-at "[0-9]") (delete-char 1))
+                       (if (looking-at " ")
+                           (delete-char 1))))))
+              (exchange-point-and-mark))))
+      (if rectify-numbering
+          (progn
+            (save-excursion
                                         ; Give some preliminary feedback:
-           (message "... reconciling numbers") (sit-for 0)
+              (message "... reconciling numbers")
                                         ; ... and renumber, in case necessary:
-           (goto-char subj-beg)
-           (if (allout-goto-prefix)
-               (allout-rebullet-heading nil    ;;; solicit
-                                         (allout-depth) ;;; depth
-                                         nil   ;;; number-control
-                                         nil   ;;; index
-                                         t))
-           (message ""))))
-    (if (not resituate)
-      (exchange-point-and-mark))))
+              (goto-char subj-beg)
+              (if (allout-goto-prefix-doublechecked)
+                  (allout-unprotected
+                   (allout-rebullet-heading nil          ;;; solicit
+                                            (allout-depth) ;;; depth
+                                            nil ;;; number-control
+                                            nil ;;; index
+                                            t)))
+              (message ""))))
+      (if (or into-bol resituate)
+          (allout-hide-by-annotation (point) (allout-mark-marker t))
+        (allout-deannotate-hidden (allout-mark-marker t) (point)))
+      (if (not resituate)
+          (exchange-point-and-mark))
+      (run-hook-with-args 'allout-structure-added-hook subj-beg subj-end))))
 ;;;_    > allout-yank (&optional arg)
 (defun allout-yank (&optional arg)
   "`allout-mode' yank, with depth and numbering adjustment of yanked topics.
@@ -3398,7 +4666,8 @@ works with normal `yank' in non-outline buffers."
 
   (interactive "*P")
   (setq this-command 'yank)
-  (yank arg)
+  (allout-unprotected
+   (yank arg))
   (if (allout-mode-p)
       (allout-yank-processing)))
 ;;;_    > allout-yank-pop (&optional arg)
@@ -3430,22 +4699,18 @@ by pops to non-distinctive yanks.  Bug..."
     (if (not (string= (allout-current-bullet) allout-file-xref-bullet))
         (error "Current heading lacks cross-reference bullet `%s'"
                allout-file-xref-bullet)
-      (let (file-name)
-        (save-excursion
-          (let* ((text-start allout-recent-prefix-end)
-                 (heading-end (progn (end-of-line) (point))))
-            (goto-char text-start)
-            (setq file-name
-                  (if (re-search-forward "\\s-\\(\\S-*\\)" heading-end t)
-                      (buffer-substring (match-beginning 1) (match-end 1))))))
-        (setq file-name
-              (if (not (= (aref file-name 0) ?:))
-                  (expand-file-name file-name)
-                                        ; A registry-files ref, strip the `:'
-                                        ; and try to follow it:
-                (let ((reg-ref (reference-registered-file
-                                (substring file-name 1) nil t)))
-                  (if reg-ref (car (cdr reg-ref))))))
+      (let ((inhibit-field-text-motion t)
+            file-name)
+        (save-match-data
+          (save-excursion
+            (let* ((text-start allout-recent-prefix-end)
+                   (heading-end (progn (end-of-line) (point))))
+              (goto-char text-start)
+              (setq file-name
+                    (if (re-search-forward "\\s-\\(\\S-*\\)" heading-end t)
+                        (buffer-substring (match-beginning 1)
+                                          (match-end 1)))))))
+        (setq file-name (expand-file-name file-name))
         (if (or (file-exists-p file-name)
                 (if (file-writable-p file-name)
                     (y-or-n-p (format "%s not there, create one? "
@@ -3453,7 +4718,7 @@ by pops to non-distinctive yanks.  Bug..."
                   (error "%s not found and can't be created" file-name)))
             (condition-case failure
                 (find-file-other-window file-name)
-              ('error failure))
+              (error failure))
           (error "%s not found" file-name))
         )
       )
@@ -3463,41 +4728,59 @@ by pops to non-distinctive yanks.  Bug..."
 ;;;_ #6 Exposure Control
 
 ;;;_  - Fundamental
+;;;_   > allout-flag-region (from to flag)
+(defun allout-flag-region (from to flag)
+  "Conceal text between FROM and TO if FLAG is non-nil, else reveal it.
+
+Exposure-change hook `allout-exposure-change-hook' is run with the same
+arguments as this function, after the exposure changes are made.  (The old
+`allout-view-change-hook' is being deprecated, and eventually will not be
+invoked.)"
+
+  ;; We use outline invisibility spec.
+  (remove-overlays from to 'category 'allout-exposure-category)
+  (when flag
+    (let ((o (make-overlay from to nil 'front-advance)))
+      (overlay-put o 'category 'allout-exposure-category)
+      (when (featurep 'xemacs)
+        (let ((props (symbol-plist 'allout-exposure-category)))
+          (while props
+            (overlay-put o (pop props) (pop props)))))))
+  (run-hooks 'allout-view-change-hook)
+  (run-hook-with-args 'allout-exposure-change-hook from to flag))
 ;;;_   > allout-flag-current-subtree (flag)
 (defun allout-flag-current-subtree (flag)
-  "Hide or show subtree of currently-visible topic.
-
-See `allout-flag-region' for more details."
+  "Conceal currently-visible topic's subtree if FLAG non-nil, else reveal it."
 
   (save-excursion
     (allout-back-to-current-heading)
+    (let ((inhibit-field-text-motion t))
+      (end-of-line))
     (allout-flag-region (point)
-                        (progn (allout-end-of-current-subtree) (1- (point)))
-                        flag)))
+                        ;; Exposing must not leave trailing blanks hidden,
+                        ;; but can leave them exposed when hiding, so we
+                        ;; can use flag's inverse as the
+                        ;; include-trailing-blank cue:
+                        (allout-end-of-current-subtree (not flag))
+                        flag)))
 
 ;;;_  - Topic-specific
 ;;;_   > allout-show-entry ()
 (defun allout-show-entry ()
-  "Like `allout-show-current-entry', reveals entries nested in hidden topics.
+  "Like `allout-show-current-entry', but reveals entries in hidden topics.
 
 This is a way to give restricted peek at a concealed locality without the
 expense of exposing its context, but can leave the outline with aberrant
-exposure.  `allout-hide-current-entry-completely' or `allout-show-to-offshoot'
-should be used after the peek to rectify the exposure."
+exposure.  `allout-show-offshoot' should be used after the peek to rectify
+the exposure."
 
   (interactive)
   (save-excursion
-    (let ((at (point))
-         beg end)
-      (allout-goto-prefix)
-      (setq beg (if (= (preceding-char) ?\r) (1- (point)) (point)))
-      (re-search-forward "[\n\r]" nil t)
-      (setq end (1- (if (< at (point))
-                       ;; We're on topic head line - show only it:
-                       (point)
-                     ;; or we're in body - include it:
-                     (max beg (or (allout-pre-next-preface) (point))))))
-      (allout-flag-region beg end ?\n)
+    (let (beg end)
+      (allout-goto-prefix-doublechecked)
+      (setq beg (if (allout-hidden-p) (1- (point)) (point)))
+      (setq end (allout-pre-next-prefix))
+      (allout-flag-region beg end nil)
       (list beg end))))
 ;;;_   > allout-show-children (&optional level strict)
 (defun allout-show-children (&optional level strict)
@@ -3518,80 +4801,95 @@ Returns point at end of subtree that was opened, if any.  (May get a
 point of non-opened subtree?)"
 
   (interactive "p")
-  (let (max-pos)
+  (let ((start-point (point)))
     (if (and (not strict)
-            (allout-hidden-p))
+             (allout-hidden-p))
 
-       (progn (allout-show-to-offshoot) ; Point's concealed, open to
-                                         ; expose it.
-              ;; Then recurse, but with "strict" set so we don't
-              ;; infinite regress:
-              (setq max-pos (allout-show-children level t)))
+        (progn (allout-show-to-offshoot) ; Point's concealed, open to
+                                        ; expose it.
+               ;; Then recurse, but with "strict" set so we don't
+               ;; infinite regress:
+               (allout-show-children level t))
 
       (save-excursion
-       (save-restriction
-         (let* ((start-pt (point))
-                (chart (allout-chart-subtree (or level 1)))
-                (to-reveal (allout-chart-to-reveal chart (or level 1))))
-           (goto-char start-pt)
-           (if (and strict (= (preceding-char) ?\r))
-               ;; Concealed root would already have been taken care of,
-               ;; unless strict was set.
-               (progn
-                 (allout-flag-region (point) (allout-snug-back) ?\n)
-                 (if allout-show-bodies
-                     (progn (goto-char (car to-reveal))
-                            (allout-show-current-entry)))))
-           (while to-reveal
-             (goto-char (car to-reveal))
-             (allout-flag-region (point) (allout-snug-back) ?\n)
-             (if allout-show-bodies
-                 (progn (goto-char (car to-reveal))
-                        (allout-show-current-entry)))
-             (setq to-reveal (cdr to-reveal)))))))))
-;;;_   > allout-hide-point-reconcile ()
-(defun allout-hide-reconcile ()
-  "Like `allout-hide-current-entry'; hides completely if within hidden region.
-
-Specifically intended for aberrant exposure states, like entries that were
-exposed by `allout-show-entry' but are within otherwise concealed regions."
-  (interactive)
-  (save-excursion
-    (allout-goto-prefix)
-    (allout-flag-region (if (not (bobp)) (1- (point)) (point))
-                         (progn (allout-pre-next-preface)
-                                (if (= ?\r (following-char))
-                                    (point)
-                                  (1- (point))))
-                         ?\r)))
+        (allout-beginning-of-current-line)
+        (save-restriction
+          (let* (depth
+                 ;; translate the level spec for this routine to the ones
+                 ;; used by -chart-subtree and -chart-to-reveal:
+                 (chart-level (cond ((not level) 1)
+                                    ((eq level t) nil)
+                                    (t level)))
+                 (chart (allout-chart-subtree chart-level))
+                 (to-reveal (or (allout-chart-to-reveal chart chart-level)
+                                ;; interactive, show discontinuous children:
+                                (and chart
+                                     (interactive-p)
+                                     (save-excursion
+                                       (allout-back-to-current-heading)
+                                       (setq depth (allout-current-depth))
+                                       (and (allout-next-heading)
+                                            (> allout-recent-depth
+                                               (1+ depth))))
+                                     (message
+                                      "Discontinuous offspring; use `%s %s'%s."
+                                      (substitute-command-keys
+                                       "\\[universal-argument]")
+                                      (substitute-command-keys
+                                       "\\[allout-shift-out]")
+                                      " to elevate them.")
+                                     (allout-chart-to-reveal
+                                      chart (- allout-recent-depth depth))))))
+            (goto-char start-point)
+            (when (and strict (allout-hidden-p))
+              ;; Concealed root would already have been taken care of,
+              ;; unless strict was set.
+              (allout-flag-region (point) (allout-snug-back) nil)
+              (when allout-show-bodies
+                (goto-char (car to-reveal))
+                (allout-show-current-entry)))
+            (while to-reveal
+              (goto-char (car to-reveal))
+              (allout-flag-region (save-excursion (allout-snug-back) (point))
+                                  (progn (search-forward "\n" nil t)
+                                         (1- (point)))
+                                  nil)
+              (when allout-show-bodies
+                (goto-char (car to-reveal))
+                (allout-show-current-entry))
+              (setq to-reveal (cdr to-reveal)))))))
+    ;; Compensate for `save-excursion's maintenance of point
+    ;; within invisible text:
+    (goto-char start-point)))
 ;;;_   > allout-show-to-offshoot ()
 (defun allout-show-to-offshoot ()
   "Like `allout-show-entry', but reveals all concealed ancestors, as well.
 
-As with `allout-hide-current-entry-completely', useful for rectifying
-aberrant exposure states produced by `allout-show-entry'."
-
+Useful for coherently exposing to a random point in a hidden region."
   (interactive)
   (save-excursion
-    (let ((orig-pt (point))
-         (orig-pref (allout-goto-prefix))
+    (let ((inhibit-field-text-motion t)
+          (orig-pt (point))
+         (orig-pref (allout-goto-prefix-doublechecked))
          (last-at (point))
-         bag-it)
-      (while (or bag-it (= (preceding-char) ?\r))
-       (beginning-of-line)
+         (bag-it 0))
+      (while (or (> bag-it 1) (allout-hidden-p))
+        (while (allout-hidden-p)
+          (move-beginning-of-line 1)
+          (if (allout-hidden-p) (forward-char -1)))
        (if (= last-at (setq last-at (point)))
-           ;; Oops, we're not making any progress!  Show the current
-           ;; topic completely, and bag this try.
+           ;; Oops, we're not making any progress!  Show the current topic
+           ;; completely, and try one more time here, if we haven't already.
            (progn (beginning-of-line)
                   (allout-show-current-subtree)
                   (goto-char orig-pt)
-                  (setq bag-it t)
-                  (beep)
-                  (message "%s: %s"
-                           "allout-show-to-offshoot: "
-                           "Aberrant nesting encountered.")))
-       (allout-show-children)
-       (goto-char orig-pref))
+                  (setq bag-it (1+ bag-it))
+                   (if (> bag-it 1)
+                       (error "allout-show-to-offshoot: %s"
+                              "Stumped by aberrant nesting.")))
+          (if (> bag-it 0) (setq bag-it 0))
+          (allout-show-children)
+          (goto-char orig-pref)))
       (goto-char orig-pt)))
   (if (allout-hidden-p)
       (allout-show-entry)))
@@ -3601,37 +4899,24 @@ aberrant exposure states produced by `allout-show-entry'."
   (interactive)
   (allout-back-to-current-heading)
   (save-excursion
-   (allout-flag-region (point)
-                        (progn (allout-end-of-current-entry) (point))
-                        ?\r)))
+    (let ((inhibit-field-text-motion t))
+      (end-of-line))
+    (allout-flag-region (point)
+                        (progn (allout-end-of-entry) (point))
+                        t)))
 ;;;_   > allout-show-current-entry (&optional arg)
 (defun allout-show-current-entry (&optional arg)
-
-  "Show body following current heading, or hide the entry if repeat count."
+  "Show body following current heading, or hide entry with universal argument."
 
   (interactive "P")
   (if arg
       (allout-hide-current-entry)
+    (save-excursion (allout-show-to-offshoot))
     (save-excursion
       (allout-flag-region (point)
-                          (progn (allout-end-of-current-entry) (point))
-                          ?\n))))
-;;;_   > allout-hide-current-entry-completely ()
-; ... allout-hide-current-entry-completely also for isearch dynamic exposure:
-(defun allout-hide-current-entry-completely ()
-  "Like `allout-hide-current-entry', but conceal topic completely.
-
-Specifically intended for aberrant exposure states, like entries that were
-exposed by `allout-show-entry' but are within otherwise concealed regions."
-  (interactive)
-  (save-excursion
-    (allout-goto-prefix)
-    (allout-flag-region (if (not (bobp)) (1- (point)) (point))
-                         (progn (allout-pre-next-preface)
-                                (if (= ?\r (following-char))
-                                    (point)
-                                  (1- (point))))
-                         ?\r)))
+                          (progn (allout-end-of-entry t) (point))
+                          nil)
+      )))
 ;;;_   > allout-show-current-subtree (&optional arg)
 (defun allout-show-current-subtree (&optional arg)
   "Show everything within the current topic.  With a repeat-count,
@@ -3644,11 +4929,31 @@ expose this topic and its siblings."
            (error "No topics")
          ;; got to first, outermost topic - set to expose it and siblings:
          (message "Above outermost topic - exposing all.")
-         (allout-flag-region (point-min)(point-max) ?\n))
+         (allout-flag-region (point-min)(point-max) nil))
+      (allout-beginning-of-current-line)
       (if (not arg)
-         (allout-flag-current-subtree ?\n)
+         (allout-flag-current-subtree nil)
        (allout-beginning-of-level)
        (allout-expose-topic '(* :))))))
+;;;_   > allout-current-topic-collapsed-p (&optional include-single-liners)
+(defun allout-current-topic-collapsed-p (&optional include-single-liners)
+  "True if the currently visible containing topic is already collapsed.
+
+Single line topics intrinsically can be considered as being both
+collapsed and uncollapsed.  If optional INCLUDE-SINGLE-LINERS is
+true, then single-line topics are considered to be collapsed.  By
+default, they are treated as being uncollapsed."
+  (save-match-data
+    (save-excursion
+      (and
+       ;; Is the topic all on one line (allowing for trailing blank line)?
+       (>= (progn (allout-back-to-current-heading)
+                  (move-end-of-line 1)
+                  (point))
+           (allout-end-of-current-subtree (not (looking-at "\n\n"))))
+
+       (or include-single-liners
+           (progn (backward-char 1) (allout-hidden-p)))))))
 ;;;_   > allout-hide-current-subtree (&optional just-close)
 (defun allout-hide-current-subtree (&optional just-close)
   "Close the current topic, or containing topic if this one is already closed.
@@ -3656,40 +4961,38 @@ expose this topic and its siblings."
 If this topic is closed and it's a top level topic, close this topic
 and its siblings.
 
-If optional arg JUST-CLOSE is non-nil, do not treat the parent or
+If optional arg JUST-CLOSE is non-nil, do not close the parent or
 siblings, even if the target topic is already closed."
 
   (interactive)
-  (let ((from (point))
-       (orig-eol (progn (end-of-line)
-                        (if (not (allout-goto-prefix))
-                            (error "No topics found")
-                          (end-of-line)(point)))))
-    (allout-flag-current-subtree ?\r)
-    (goto-char from)
-    (if (and (= orig-eol (progn (goto-char orig-eol)
-                               (end-of-line)
-                               (point)))
-            (not just-close)
-             ;; Structure didn't change - try hiding current level:
-            (goto-char from)
-            (if (allout-up-current-level 1 t)
-                t
-              (goto-char 0)
-              (let ((msg
-                     "Top-level topic already closed - closing siblings..."))
-                (message msg)
-                (allout-expose-topic '(0 :))
-                (message (concat msg "  Done.")))
-              nil)
-            (/= (allout-recent-depth) 0))
-       (allout-hide-current-subtree))
-      (goto-char from)))
+  (let* ((from (point))
+         (sibs-msg "Top-level topic already closed - closing siblings...")
+         (current-exposed (not (allout-current-topic-collapsed-p t))))
+    (cond (current-exposed (allout-flag-current-subtree t))
+          (just-close nil)
+          ((allout-ascend) (allout-hide-current-subtree))
+          (t (goto-char 0)
+             (message sibs-msg)
+             (allout-goto-prefix-doublechecked)
+             (allout-expose-topic '(0 :))
+             (message (concat sibs-msg "  Done."))))
+    (goto-char from)))
+;;;_   > allout-toggle-current-subtree-exposure
+(defun allout-toggle-current-subtree-exposure ()
+  "Show or hide the current subtree depending on its current state."
+  ;; thanks to tassilo for suggesting this.
+  (interactive)
+  (save-excursion
+    (allout-back-to-heading)
+    (if (allout-hidden-p (point-at-eol))
+        (allout-show-current-subtree)
+      (allout-hide-current-subtree))))
 ;;;_   > allout-show-current-branches ()
 (defun allout-show-current-branches ()
   "Show all subheadings of this heading, but not their bodies."
   (interactive)
-  (beginning-of-line)
+  (let ((inhibit-field-text-motion t))
+    (beginning-of-line))
   (allout-show-children t))
 ;;;_   > allout-hide-current-leaves ()
 (defun allout-hide-current-leaves ()
@@ -3705,7 +5008,7 @@ siblings, even if the target topic is already closed."
   "Show all of the text in the buffer."
   (interactive)
   (message "Exposing entire buffer...")
-  (allout-flag-region (point-min) (point-max) ?\n)
+  (allout-flag-region (point-min) (point-max) nil)
   (message "Exposing entire buffer...  Done."))
 ;;;_   > allout-hide-bodies ()
 (defun allout-hide-bodies ()
@@ -3715,17 +5018,19 @@ siblings, even if the target topic is already closed."
 ;;;_   > allout-hide-region-body (start end)
 (defun allout-hide-region-body (start end)
   "Hide all body lines in the region, but not headings."
-  (save-excursion
-    (save-restriction
-      (narrow-to-region start end)
-      (goto-char (point-min))
-      (while (not (eobp))
-       (allout-flag-region (point)
-                             (progn (allout-pre-next-preface) (point)) ?\r)
-       (if (not (eobp))
-           (forward-char
-            (if (looking-at "[\n\r][\n\r]")
-                2 1)))))))
+  (save-match-data
+    (save-excursion
+      (save-restriction
+        (narrow-to-region start end)
+        (goto-char (point-min))
+        (let ((inhibit-field-text-motion t))
+          (while (not (eobp))
+            (end-of-line)
+            (allout-flag-region (point) (allout-end-of-entry) t)
+            (if (not (eobp))
+                (forward-char
+                 (if (looking-at "\n\n")
+                     2 1)))))))))
 
 ;;;_   > allout-expose-topic (spec)
 (defun allout-expose-topic (spec)
@@ -3742,7 +5047,7 @@ SPEC is either a number or a list.
 
 Successive specs on a list are applied to successive sibling topics.
 
-A simple spec \(either a number, one of a few symbols, or the null
+A simple spec (either a number, one of a few symbols, or the null
 list) dictates the exposure for the corresponding topic.
 
 Non-null lists recursively designate exposure specs for respective
@@ -3765,7 +5070,7 @@ Simple (numeric and null-list) specs are interpreted as follows:
        apply prior element to all siblings at current level, *up to*
        those siblings that would be covered by specs following the `:'
        on the list.  Ie, apply to all topics at level but the last
-       ones.  \(Only first of multiple colons at same level is
+       ones.  (Only first of multiple colons at same level is
        respected - subsequent ones are discarded.)
   *  - completely opens the topic, including bodies.
   +  - shows all the sub headers, but not the bodies
@@ -3791,9 +5096,7 @@ Examples:
     (let ((depth (allout-depth))
          (max-pos 0)
          prev-elem curr-elem
-         stay done
-         snug-back
-         )
+         stay)
       (while spec
        (setq prev-elem curr-elem
              curr-elem (car spec)
@@ -3804,7 +5107,10 @@ Examples:
          (cond ((eq curr-elem '*) (allout-show-current-subtree)
                 (if (> allout-recent-end-of-subtree max-pos)
                     (setq max-pos allout-recent-end-of-subtree)))
-               ((eq curr-elem '+) (allout-show-current-branches)
+                ((eq curr-elem '+)
+                 (if (not (allout-hidden-p))
+                     (save-excursion (allout-hide-current-subtree t)))
+                 (allout-show-current-branches)
                 (if (> allout-recent-end-of-subtree max-pos)
                     (setq max-pos allout-recent-end-of-subtree)))
                ((eq curr-elem '-) (allout-show-current-entry))
@@ -3821,7 +5127,7 @@ Examples:
                       (setq spec (append (make-list residue prev-elem)
                                          spec)))))))
         ((numberp curr-elem)
-         (if (and (>= 0 curr-elem) (allout-visible-p))
+         (if (and (>= 0 curr-elem) (not (allout-hidden-p)))
              (save-excursion (allout-hide-current-subtree t)
                              (if (> 0 curr-elem)
                                  nil
@@ -3846,7 +5152,11 @@ Examples:
       max-pos)))
 ;;;_   > allout-old-expose-topic (spec &rest followers)
 (defun allout-old-expose-topic (spec &rest followers)
-  "Dictate wholesale exposure scheme for current topic, according to SPEC.
+
+  "Deprecated.  Use `allout-expose-topic' (with different schema
+format) instead.
+
+Dictate wholesale exposure scheme for current topic, according to SPEC.
 
 SPEC is either a number or a list.  Optional successive args
 dictate exposure for subsequent siblings of current topic.
@@ -3876,8 +5186,8 @@ for the corresponding offspring of the topic.
 Optional FOLLOWERS arguments dictate exposure for succeeding siblings."
 
   (interactive "xExposure spec: ")
-  (let ((depth (allout-current-depth))
-       done
+  (let ((inhibit-field-text-motion t)
+        (depth (allout-current-depth))
        max-pos)
     (cond ((null spec) nil)
          ((symbolp spec)
@@ -3918,9 +5228,6 @@ Optional FOLLOWERS arguments dictate exposure for succeeding siblings."
       (allout-old-expose-topic (car followers))
       (setq followers (cdr followers)))
     max-pos))
-(make-obsolete 'allout-old-expose-topic
-               "use `allout-expose-topic' (with different schema format) instead."
-               "19.23")
 ;;;_   > allout-new-exposure '()
 (defmacro allout-new-exposure (&rest spec)
   "Literal frontend for `allout-expose-topic', doesn't evaluate arguments.
@@ -3929,6 +5236,8 @@ need not be quoted in `allout-new-exposure'.
 
 Cursor is left at start position.
 
+Use this instead of obsolete `allout-exposure'.
+
 Examples:
 \(allout-new-exposure (-1 () () () 1) 0)
        Close current topic at current level so only the immediate
@@ -3943,7 +5252,7 @@ Examples:
        level, and expose children of subsequent topics at current
        level *except* for the last, which should be opened completely."
   (list 'save-excursion
-       '(if (not (or (allout-goto-prefix)
+       '(if (not (or (allout-goto-prefix-doublechecked)
                      (allout-next-heading)))
             (error "allout-new-exposure: Can't find any outline topics"))
        (list 'allout-expose-topic (list 'quote spec))))
@@ -4041,7 +5350,7 @@ the prefix:
 
  list - Present prefix as numeric section.subsection..., starting with
        section indicated by the list, innermost nesting first.
- `indent' \(symbol) -  Convert header prefixes to all white space,
+ `indent' (symbol) -  Convert header prefixes to all white space,
                       except for distinctive bullets.
 
 The elements of the list produced are lists that represents a topic
@@ -4057,27 +5366,28 @@ header and body.  The elements of that list are:
   (interactive "r")
   (save-excursion
     (let*
-       ;; state vars:
-       (strings prefix pad result depth new-depth out gone-out bullet beg
+        ((inhibit-field-text-motion t)
+         ;; state vars:
+         strings prefix result depth new-depth out gone-out bullet beg
                 next done)
 
       (goto-char start)
       (beginning-of-line)
       ;; Goto initial topic, and register preceeding stuff, if any:
-      (if (> (allout-goto-prefix) start)
+      (if (> (allout-goto-prefix-doublechecked) start)
          ;; First topic follows beginning point - register preliminary stuff:
          (setq result (list (list 0 "" nil
                                   (buffer-substring start (1- (point)))))))
       (while (and (not done)
                  (not (eobp))          ; Loop until we've covered the region.
                  (not (> (point) end)))
-       (setq depth (allout-recent-depth)       ; Current topics depth,
+       (setq depth allout-recent-depth         ; Current topics depth,
              bullet (allout-recent-bullet)     ; ... bullet,
              prefix (allout-recent-prefix)
              beg (progn (allout-end-of-prefix t) (point))) ; and beginning.
        (setq done                      ; The boundary for the current topic:
              (not (allout-next-visible-heading 1)))
-       (setq new-depth (allout-recent-depth))
+       (setq new-depth allout-recent-depth)
        (setq gone-out out
              out (< new-depth depth))
        (beginning-of-line)
@@ -4090,16 +5400,12 @@ header and body.  The elements of that list are:
                       beg
                                        ;To hidden text or end of line:
                       (progn
-                        (search-forward "\r"
-                                        (save-excursion (end-of-line)
-                                                        (point))
-                                        1)
-                        (if (= (preceding-char) ?\r)
-                            (1- (point))
-                          (point))))
+                         (end-of-line)
+                         (allout-back-to-visible-text)))
                      strings))
-         (if (< (point) next)          ; Resume from after hid text, if any.
-             (forward-line 1))
+         (when (< (point) next)      ; Resume from after hid text, if any.
+            (line-move 1)
+            (beginning-of-line))
          (setq beg (point)))
        ;; Accumulate list for this topic:
        (setq strings (nreverse strings))
@@ -4151,13 +5457,20 @@ header and body.  The elements of that list are:
                                 (cdr format)))))))
       ;; Put the list with first at front, to last at back:
       (nreverse result))))
+;;;_   > my-region-active-p ()
+(defmacro my-region-active-p ()
+  (if (fboundp 'region-active-p)
+      '(region-active-p)
+    'mark-active))
 ;;;_   > allout-process-exposed (&optional func from to frombuf
 ;;;                                        tobuf format)
 (defun allout-process-exposed (&optional func from to frombuf tobuf
                                          format start-num)
   "Map function on exposed parts of current topic; results to another buffer.
 
-Apply FUNC to exposed portions FROM position TO position in buffer
+All args are options; default values itemized below.
+
+Apply FUNCTION to exposed portions FROM position TO position in buffer
 FROMBUF to buffer TOBUF.  Sixth optional arg, FORMAT, designates an
 alternate presentation form:
 
@@ -4166,11 +5479,11 @@ alternate presentation form:
  X`flat-indented' - Prefix is like `flat' for first topic at each
  X                level, but subsequent topics have only leaf topic
  X                number, padded with blanks to line up with first.
- `indent' \(symbol) -  Convert header prefixes to all white space,
+ `indent' (symbol) -  Convert header prefixes to all white space,
                       except for distinctive bullets.
 
 Defaults:
-  FUNC:                `allout-insert-listified'
+  FUNCTION:    `allout-insert-listified'
   FROM:                region start, if region active, else start of buffer
   TO:          region end, if region active, else end of buffer
   FROMBUF:     current buffer
@@ -4207,7 +5520,7 @@ Defaults:
          (progn (set-buffer frombuf)
                 (allout-listify-exposed from to format))))
     (set-buffer tobuf)
-    (mapcar func listified)
+    (mapc func listified)
     (pop-to-buffer tobuf)))
 
 ;;;_  - Copy exposed
@@ -4219,9 +5532,7 @@ LISTIFIED is a list representing each topic header and body:
 
  \`(depth prefix text)'
 
-or
-
- \`(depth prefix text bullet-plus)'
+or \`(depth prefix text bullet-plus)'
 
 If `bullet-plus' is specified, it is inserted just after the entire prefix."
   (setq listified (cdr listified))
@@ -4320,7 +5631,7 @@ used verbatim."
 ;;;_   > allout-latex-verb-quote (string &optional flow)
 (defun allout-latex-verb-quote (string &optional flow)
   "Return copy of STRING for literal reproduction across LaTeX processing.
-Expresses the original characters \(including carriage returns) of the
+Expresses the original characters (including carriage returns) of the
 string across LaTeX processing."
   (mapconcat (function
              (lambda (char)
@@ -4332,23 +5643,25 @@ string across LaTeX processing."
             ""))
 ;;;_   > allout-latex-verbatim-quote-curr-line ()
 (defun allout-latex-verbatim-quote-curr-line ()
-  "Express line for exact \(literal) representation across LaTeX processing.
+  "Express line for exact (literal) representation across LaTeX processing.
 
-Adjust line contents so it is unaltered \(from the original line)
+Adjust line contents so it is unaltered (from the original line)
 across LaTeX processing, within the context of a `verbatim'
 environment.  Leaves point at the end of the line."
-  (beginning-of-line)
-  (let ((beg (point))
-       (end (progn (end-of-line)(point))))
-    (goto-char beg)
-    (while (re-search-forward "\\\\"
-           ;;"\\\\\\|\\{\\|\\}\\|\\_\\|\\$\\|\\\"\\|\\&\\|\\^\\|\\-\\|\\*\\|#"
-                             end       ; bounded by end-of-line
-                             1)        ; no matches, move to end & return nil
-      (goto-char (match-beginning 0))
-      (insert "\\")
-      (setq end (1+ end))
-      (goto-char (1+ (match-end 0))))))
+  (let ((inhibit-field-text-motion t))
+    (beginning-of-line)
+    (let ((beg (point))
+          (end (progn (end-of-line)(point))))
+      (goto-char beg)
+      (save-match-data
+        (while (re-search-forward "\\\\"
+  ;;"\\\\\\|\\{\\|\\}\\|\\_\\|\\$\\|\\\"\\|\\&\\|\\^\\|\\-\\|\\*\\|#"
+                                  end  ; bounded by end-of-line
+                                  1) ; no matches, move to end & return nil
+          (goto-char (match-beginning 2))
+          (insert "\\")
+          (setq end (1+ end))
+          (goto-char (1+ (match-end 2))))))))
 ;;;_   > allout-insert-latex-header (buffer)
 (defun allout-insert-latex-header (buffer)
   "Insert initial LaTeX commands at point in BUFFER."
@@ -4360,13 +5673,6 @@ environment.  Leaves point at the end of the line."
        (page-numbering (if allout-number-pages
                            "\\pagestyle{empty}\n"
                          ""))
-       (linesdef (concat "\\def\\beginlines{"
-                         "\\par\\begingroup\\nobreak\\medskip"
-                         "\\parindent=0pt\n"
-                         " \\kern1pt\\nobreak \\obeylines \\obeyspaces "
-                         "\\everypar{\\strut}}\n"
-                         "\\def\\endlines{"
-                         "\\kern1pt\\endgroup\\medbreak\\noindent}\n"))
        (titlecmd (format "\\newcommand{\\titlecmd}[1]{{%s #1}}\n"
                          allout-title-style))
        (labelcmd (format "\\newcommand{\\labelcmd}[1]{{%s #1}}\n"
@@ -4399,9 +5705,9 @@ environment.  Leaves point at the end of the line."
        (title (format "%s%s%s%s"
                       "\\titlecmd{"
                       (allout-latex-verb-quote (if allout-title
-                                               (condition-case err
+                                               (condition-case nil
                                                    (eval allout-title)
-                                                 ('error "<unnamed buffer>"))
+                                                 (error "<unnamed buffer>"))
                                              "Unnamed Outline"))
                       "}\n"
                       "\\end{center}\n\n"))
@@ -4440,14 +5746,14 @@ BULLET string, and a list of TEXT strings for the body."
         body-content bop)
                                        ; Do the head line:
     (insert (concat "\\OneHeadLine{\\verb\1 "
-                   (allout-latex-verb-quote bullet)
-                   "\1}{"
-                   depth
-                   "}{\\verb\1 "
-                   (if head-line
-                       (allout-latex-verb-quote head-line)
-                     "")
-                   "\1}\n"))
+                    (allout-latex-verb-quote bullet)
+                    "\1}{"
+                    depth
+                    "}{\\verb\1 "
+                    (if head-line
+                        (allout-latex-verb-quote head-line)
+                      "")
+                    "\1}\n"))
     (if (not body-lines)
        nil
       ;;(insert "\\beginlines\n")
@@ -4509,19 +5815,773 @@ With repeat count, copy the exposed portions of entire buffer."
     (pop-to-buffer buf)
     (goto-char start-pt)))
 
-;;;_ #8 miscellaneous
-;;;_  > allout-mark-topic ()
-(defun allout-mark-topic ()
-  "Put the region around topic currently containing point."
-  (interactive)
-  (beginning-of-line)
-  (allout-goto-prefix)
-  (push-mark (point))
-  (allout-end-of-current-subtree)
-  (exchange-point-and-mark))
-;;;_  > outlineify-sticky ()
+;;;_ #8 Encryption
+;;;_  > allout-toggle-current-subtree-encryption (&optional fetch-pass)
+(defun allout-toggle-current-subtree-encryption (&optional fetch-pass)
+  "Encrypt clear or decrypt encoded text of visibly-containing topic's contents.
+
+Optional FETCH-PASS universal argument provokes key-pair encryption with
+single universal argument.  With doubled universal argument (value = 16),
+it forces prompting for the passphrase regardless of availability from the
+passphrase cache.  With no universal argument, the appropriate passphrase
+is obtained from the cache, if available, else from the user.
+
+Only GnuPG encryption is supported.
+
+\*NOTE WELL* that the encrypted text must be ascii-armored.  For gnupg
+encryption, include the option ``armor'' in your ~/.gnupg/gpg.conf file.
+
+Both symmetric-key and key-pair encryption is implemented.  Symmetric is
+the default, use a single (x4) universal argument for keypair mode.
+
+Encrypted topic's bullet is set to a `~' to signal that the contents of the
+topic (body and subtopics, but not heading) is pending encryption or
+encrypted.  `*' asterisk immediately after the bullet signals that the body
+is encrypted, its' absence means the topic is meant to be encrypted but is
+not.  When a file with topics pending encryption is saved, topics pending
+encryption are encrypted.  See allout-encrypt-unencrypted-on-saves for
+auto-encryption specifics.
+
+\*NOTE WELL* that automatic encryption that happens during saves will
+default to symmetric encryption - you must deliberately (re)encrypt key-pair
+encrypted topics if you want them to continue to use the key-pair cipher.
+
+Level-one topics, with prefix consisting solely of an `*' asterisk, cannot be
+encrypted.  If you want to encrypt the contents of a top-level topic, use
+\\[allout-shift-in] to increase its depth.
+
+  Passphrase Caching
+
+The encryption passphrase is solicited if not currently available in the
+passphrase cache from a recent encryption action.
+
+The solicited passphrase is retained for reuse in a cache, if enabled.  See
+`pgg-cache-passphrase' and `pgg-passphrase-cache-expiry' for details.
+
+  Symmetric Passphrase Hinting and Verification
+
+If the file previously had no associated passphrase, or had a different
+passphrase than specified, the user is prompted to repeat the new one for
+corroboration.  A random string encrypted by the new passphrase is set on
+the buffer-specific variable `allout-passphrase-verifier-string', for
+confirmation of the passphrase when next obtained, before encrypting or
+decrypting anything with it.  This helps avoid mistakenly shifting between
+keys.
+
+If allout customization var `allout-passphrase-verifier-handling' is
+non-nil, an entry for `allout-passphrase-verifier-string' and its value is
+added to an Emacs 'local variables' section at the end of the file, which
+is created if necessary.  That setting is for retention of the passphrase
+verifier across Emacs sessions.
+
+Similarly, `allout-passphrase-hint-string' stores a user-provided reminder
+about their passphrase, and `allout-passphrase-hint-handling' specifies
+when the hint is presented, or if passphrase hints are disabled.  If
+enabled (see the `allout-passphrase-hint-handling' docstring for details),
+the hint string is stored in the local-variables section of the file, and
+solicited whenever the passphrase is changed."
+  (interactive "P")
+  (save-excursion
+    (allout-back-to-current-heading)
+    (allout-toggle-subtree-encryption fetch-pass)
+    )
+  )
+;;;_  > allout-toggle-subtree-encryption (&optional fetch-pass)
+(defun allout-toggle-subtree-encryption (&optional fetch-pass)
+  "Encrypt clear text or decrypt encoded topic contents (body and subtopics.)
+
+Optional FETCH-PASS universal argument provokes key-pair encryption with
+single universal argument.  With doubled universal argument (value = 16),
+it forces prompting for the passphrase regardless of availability from the
+passphrase cache.  With no universal argument, the appropriate passphrase
+is obtained from the cache, if available, else from the user.
+
+Currently only GnuPG encryption is supported, and integration
+with gpg-agent is not yet implemented.
+
+\**NOTE WELL** that the encrypted text must be ascii-armored.  For gnupg
+encryption, include the option ``armor'' in your ~/.gnupg/gpg.conf file.
+
+See `allout-toggle-current-subtree-encryption' for more details."
+
+  (interactive "P")
+  (save-excursion
+    (allout-end-of-prefix t)
+
+    (if (= allout-recent-depth 1)
+        (error (concat "Cannot encrypt or decrypt level 1 topics -"
+                       " shift it in to make it encryptable")))
+
+    (let* ((allout-buffer (current-buffer))
+           ;; Assess location:
+           (bullet-pos allout-recent-prefix-beginning)
+           (after-bullet-pos (point))
+           (was-encrypted
+            (progn (if (= (point-max) after-bullet-pos)
+                       (error "no body to encrypt"))
+                   (allout-encrypted-topic-p)))
+           (was-collapsed (if (not (search-forward "\n" nil t))
+                              nil
+                            (backward-char 1)
+                            (allout-hidden-p)))
+           (subtree-beg (1+ (point)))
+           (subtree-end (allout-end-of-subtree))
+           (subject-text (buffer-substring-no-properties subtree-beg
+                                                         subtree-end))
+           (subtree-end-char (char-after (1- subtree-end)))
+           (subtree-trailing-char (char-after subtree-end))
+           ;; kluge - result-text needs to be nil, but we also want to
+           ;;         check for the error condition
+           (result-text (if (or (string= "" subject-text)
+                                (string= "\n" subject-text))
+                            (error "No topic contents to %scrypt"
+                                   (if was-encrypted "de" "en"))
+                          nil))
+           ;; Assess key parameters:
+           (key-info (or
+                      ;; detect the type by which it is already encrypted
+                      (and was-encrypted
+                           (allout-encrypted-key-info subject-text))
+                      (and (member fetch-pass '(4 (4)))
+                           '(keypair nil))
+                      '(symmetric nil)))
+           (for-key-type (car key-info))
+           (for-key-identity (cadr key-info))
+           (fetch-pass (and fetch-pass (member fetch-pass '(16 (16)))))
+           (was-coding-system buffer-file-coding-system))
+
+      (when (not was-encrypted)
+        ;; ensure that non-ascii chars pending encryption are noticed before
+        ;; they're encrypted, so the coding system is set to accomodate
+        ;; them.
+        (setq buffer-file-coding-system
+              (select-safe-coding-system subtree-beg subtree-end))
+        ;; if the coding system for the text being encrypted is different
+        ;; than that prevailing, then there a real risk that the coding
+        ;; system can't be noticed by emacs when the file is visited.  to
+        ;; mitigate that, offer to preserve the coding system using a file
+        ;; local variable.
+        (if (and (not (equal buffer-file-coding-system
+                             was-coding-system))
+                 (yes-or-no-p
+                  (format (concat "Register coding system %s as file local"
+                                  " var?  Necessary when only encrypted text"
+                                  " is in that coding system. ")
+                          buffer-file-coding-system)))
+            (allout-adjust-file-variable "buffer-file-coding-system"
+                                         buffer-file-coding-system)))
+
+      (setq result-text
+            (allout-encrypt-string subject-text was-encrypted
+                                    (current-buffer)
+                                    for-key-type for-key-identity fetch-pass))
+
+       ;; Replace the subtree with the processed product.
+      (allout-unprotected
+       (progn
+         (set-buffer allout-buffer)
+         (delete-region subtree-beg subtree-end)
+         (insert result-text)
+         (if was-collapsed
+             (allout-flag-region (1- subtree-beg) (point) t))
+         ;; adjust trailing-blank-lines to preserve topic spacing:
+         (if (not was-encrypted)
+             (if (and (= subtree-end-char ?\n)
+                      (= subtree-trailing-char ?\n))
+                 (insert subtree-trailing-char)))
+         ;; Ensure that the item has an encrypted-entry bullet:
+         (if (not (string= (buffer-substring-no-properties
+                            (1- after-bullet-pos) after-bullet-pos)
+                           allout-topic-encryption-bullet))
+             (progn (goto-char (1- after-bullet-pos))
+                    (delete-char 1)
+                    (insert allout-topic-encryption-bullet)))
+         (if was-encrypted
+             ;; Remove the is-encrypted bullet qualifier:
+             (progn (goto-char after-bullet-pos)
+                    (delete-char 1))
+           ;; Add the is-encrypted bullet qualifier:
+           (goto-char after-bullet-pos)
+           (insert "*"))))
+      (run-hook-with-args 'allout-structure-added-hook
+                          bullet-pos subtree-end))))
+;;;_  > allout-encrypt-string (text decrypt allout-buffer key-type for-key
+;;;                                  fetch-pass &optional retried verifying
+;;;                                  passphrase)
+(defun allout-encrypt-string (text decrypt allout-buffer key-type for-key
+                                   fetch-pass &optional retried rejected
+                                   verifying passphrase)
+  "Encrypt or decrypt message TEXT.
+
+If DECRYPT is true (default false), then decrypt instead of encrypt.
+
+FETCH-PASS (default false) forces fresh prompting for the passphrase.
+
+KEY-TYPE indicates whether to use a 'symmetric or 'keypair cipher.
+
+FOR-KEY is human readable identification of the first of the user's
+eligible secret keys a keypair decryption targets, or else nil.
+
+Optional RETRIED is for internal use - conveys the number of failed keys
+that have been solicited in sequence leading to this current call.
+
+Optional PASSPHRASE enables explicit delivery of the decryption passphrase,
+for verification purposes.
+
+Optional REJECTED is for internal use - conveys the number of
+rejections due to matches against
+`allout-encryption-ciphertext-rejection-regexps', as limited by
+`allout-encryption-ciphertext-rejection-ceiling'.
+
+Returns the resulting string, or nil if the transformation fails."
+
+  (require 'pgg)
+
+  (if (not (fboundp 'pgg-encrypt-symmetric))
+      (error "Allout encryption depends on a newer version of pgg"))
+
+  (let* ((scheme (upcase
+                  (format "%s" (or pgg-scheme pgg-default-scheme "GPG"))))
+         (for-key (and (equal key-type 'keypair)
+                       (or for-key
+                           (split-string (read-string
+                                          (format "%s message recipients: "
+                                                  scheme))
+                                         "[ \t,]+"))))
+         (target-prompt-id (if (equal key-type 'keypair)
+                               (if (= (length for-key) 1)
+                                   (car for-key) for-key)
+                             (buffer-name allout-buffer)))
+         (target-cache-id (format "%s-%s"
+                                  key-type
+                                  (if (equal key-type 'keypair)
+                                      target-prompt-id
+                                    (or (buffer-file-name allout-buffer)
+                                        target-prompt-id))))
+         (encoding (with-current-buffer allout-buffer
+                     buffer-file-coding-system))
+         (multibyte (with-current-buffer allout-buffer
+                     enable-multibyte-characters))
+         (strip-plaintext-regexps
+          (if (not decrypt)
+              (allout-get-configvar-values
+               'allout-encryption-plaintext-sanitization-regexps)))
+         (reject-ciphertext-regexps
+          (if (not decrypt)
+              (allout-get-configvar-values
+               'allout-encryption-ciphertext-rejection-regexps)))
+         (rejected (or rejected 0))
+         (rejections-left (- allout-encryption-ciphertext-rejection-ceiling
+                             rejected))
+         result-text status
+         )
+
+    (if (and fetch-pass (not passphrase))
+        ;; Force later fetch by evicting passphrase from the cache.
+        (pgg-remove-passphrase-from-cache target-cache-id t))
+
+    (catch 'encryption-failed
+
+        ;; We handle only symmetric-key passphrase caching.
+        (if (and (not passphrase)
+                 (not (equal key-type 'keypair)))
+            (setq passphrase (allout-obtain-passphrase for-key
+                                                       target-cache-id
+                                                       target-prompt-id
+                                                       key-type
+                                                       allout-buffer
+                                                       retried fetch-pass)))
+
+        (with-temp-buffer
+
+          (insert text)
+
+          ;; convey the text characteristics of the original buffer:
+          (set-buffer-multibyte multibyte)
+          (when encoding
+            (set-buffer-file-coding-system encoding)
+            (if (not decrypt)
+                (encode-coding-region (point-min) (point-max) encoding)))
+
+          (when (and strip-plaintext-regexps (not decrypt))
+            (dolist (re strip-plaintext-regexps)
+              (let ((re (if (listp re) (car re) re))
+                    (replacement (if (listp re) (cadr re) "")))
+                (goto-char (point-min))
+                (save-match-data
+                  (while (re-search-forward re nil t)
+                    (replace-match replacement nil nil))))))
+
+          (cond
+
+           ;; symmetric:
+           ((equal key-type 'symmetric)
+            (setq status
+                  (if decrypt
+
+                      (pgg-decrypt (point-min) (point-max) passphrase)
+
+                    (pgg-encrypt-symmetric (point-min) (point-max)
+                                           passphrase)))
+
+            (if status
+                (pgg-situate-output (point-min) (point-max))
+              ;; failed - handle passphrase caching
+              (if verifying
+                  (throw 'encryption-failed nil)
+                (pgg-remove-passphrase-from-cache target-cache-id t)
+                (error "Symmetric-cipher %scryption failed - %s"
+                       (if decrypt "de" "en")
+                       "try again with different passphrase."))))
+
+           ;; encrypt 'keypair:
+           ((not decrypt)
+
+            (setq status
+
+                  (pgg-encrypt for-key
+                               nil (point-min) (point-max) passphrase))
+
+            (if status
+                (pgg-situate-output (point-min) (point-max))
+              (error (pgg-remove-passphrase-from-cache target-cache-id t)
+                     (error "encryption failed"))))
+
+           ;; decrypt 'keypair:
+           (t
+
+            (setq status
+                  (pgg-decrypt (point-min) (point-max) passphrase))
+
+            (if status
+                (pgg-situate-output (point-min) (point-max))
+              (error (pgg-remove-passphrase-from-cache target-cache-id t)
+                     (error "decryption failed")))))
+
+          (setq result-text
+                (buffer-substring-no-properties
+                 1 (- (point-max) (if decrypt 0 1))))
+          )
+
+        ;; validate result - non-empty
+        (cond ((not result-text)
+               (if verifying
+                   nil
+                 ;; transform was fruitless, retry w/new passphrase.
+                 (pgg-remove-passphrase-from-cache target-cache-id t)
+                 (allout-encrypt-string text decrypt allout-buffer
+                                        key-type for-key nil
+                                        (if retried (1+ retried) 1)
+                                        rejected verifying nil)))
+
+              ;; Retry (within limit) if ciphertext contains rejections:
+              ((and (not decrypt)
+                    ;; Check for disqualification of this ciphertext:
+                    (let ((regexps reject-ciphertext-regexps)
+                          reject-it)
+                      (while (and regexps (not reject-it))
+                        (setq reject-it (string-match (car regexps)
+                                                      result-text))
+                        (pop regexps))
+                      reject-it))
+               (setq rejections-left (1- rejections-left))
+               (if (<= rejections-left 0)
+                   (error (concat "Ciphertext rejected too many times"
+                                  " (%s), per `%s'")
+                          allout-encryption-ciphertext-rejection-ceiling
+                          'allout-encryption-ciphertext-rejection-regexps)
+                 (allout-encrypt-string text decrypt allout-buffer
+                                        key-type for-key nil
+                                        retried (1+ rejected)
+                                        verifying passphrase)))
+              ;; Barf if encryption yields extraordinary control chars:
+              ((and (not decrypt)
+                    (string-match "[\C-a\C-k\C-o-\C-z\C-@]"
+                                  result-text))
+               (error (concat "Encryption produced non-armored text, which"
+                              "conflicts with allout mode - reconfigure!")))
+
+              ;; valid result and just verifying or non-symmetric:
+              ((or verifying (not (equal key-type 'symmetric)))
+               (if (or verifying decrypt)
+                   (pgg-add-passphrase-to-cache target-cache-id
+                                                passphrase t))
+               result-text)
+
+              ;; valid result and regular symmetric - "register"
+              ;; passphrase with mnemonic aids/cache.
+              (t
+               (set-buffer allout-buffer)
+               (if passphrase
+                   (pgg-add-passphrase-to-cache target-cache-id
+                                                passphrase t))
+               (allout-update-passphrase-mnemonic-aids for-key passphrase
+                                                       allout-buffer)
+               result-text)
+              )
+        )
+    )
+  )
+;;;_  > allout-obtain-passphrase (for-key cache-id prompt-id key-type
+;;;                                       allout-buffer retried fetch-pass)
+(defun allout-obtain-passphrase (for-key cache-id prompt-id key-type
+                                         allout-buffer retried fetch-pass)
+  "Obtain passphrase for a key from the cache or else from the user.
+
+When obtaining from the user, symmetric-cipher passphrases are verified
+against either, if available and enabled, a random string that was
+encrypted against the passphrase, or else against repeated entry by the
+user for corroboration.
+
+FOR-KEY is the key for which the passphrase is being obtained.
+
+CACHE-ID is the cache id of the key for the passphrase.
+
+PROMPT-ID is the id for use when prompting the user.
+
+KEY-TYPE is either 'symmetric or 'keypair.
+
+ALLOUT-BUFFER is the buffer containing the entry being en/decrypted.
+
+RETRIED is the number of this attempt to obtain this passphrase.
+
+FETCH-PASS causes the passphrase to be solicited from the user, regardless
+of the availability of a cached copy."
+
+  (if (not (equal key-type 'symmetric))
+      ;; do regular passphrase read on non-symmetric passphrase:
+      (pgg-read-passphrase (format "%s passphrase%s: "
+                                   (upcase (format "%s" (or pgg-scheme
+                                                            pgg-default-scheme
+                                                            "GPG")))
+                                     (if prompt-id
+                                         (format " for %s" prompt-id)
+                                       ""))
+                           cache-id t)
+
+    ;; Symmetric hereon:
+
+    (save-excursion
+      (set-buffer allout-buffer)
+      (let* ((hint (if (and (not (string= allout-passphrase-hint-string ""))
+                            (or (equal allout-passphrase-hint-handling 'always)
+                                (and (equal allout-passphrase-hint-handling
+                                            'needed)
+                                     retried)))
+                       (format " [%s]" allout-passphrase-hint-string)
+                     ""))
+             (retry-message (if retried (format " (%s retry)" retried) ""))
+             (prompt-sans-hint (format "'%s' symmetric passphrase%s: "
+                                       prompt-id retry-message))
+             (full-prompt (format "'%s' symmetric passphrase%s%s: "
+                                  prompt-id hint retry-message))
+             (prompt full-prompt)
+             (verifier-string (allout-get-encryption-passphrase-verifier))
+
+             (cached (and (not fetch-pass)
+                          (pgg-read-passphrase-from-cache cache-id t)))
+             (got-pass (or cached
+                           (pgg-read-passphrase full-prompt cache-id t)))
+             confirmation)
+
+        (if (not got-pass)
+            nil
+
+          ;; Duplicate our handle on the passphrase so it's not clobbered by
+          ;; deactivate-passwd memory clearing:
+          (setq got-pass (copy-sequence got-pass))
+
+          (cond (verifier-string
+                 (save-window-excursion
+                   (if (allout-encrypt-string verifier-string 'decrypt
+                                              allout-buffer 'symmetric
+                                              for-key nil 0 0 'verifying
+                                              (copy-sequence got-pass))
+                       (setq confirmation (format "%s" got-pass))))
+
+                 (if (and (not confirmation)
+                          (if (yes-or-no-p
+                               (concat "Passphrase differs from established"
+                                       " - use new one instead? "))
+                              ;; deactivate password for subsequent
+                              ;; confirmation:
+                              (progn
+                                (pgg-remove-passphrase-from-cache cache-id t)
+                                (setq prompt prompt-sans-hint)
+                                nil)
+                            t))
+                     (progn (pgg-remove-passphrase-from-cache cache-id t)
+                            (error "Wrong passphrase."))))
+                ;; No verifier string - force confirmation by repetition of
+                ;; (new) passphrase:
+                ((or fetch-pass (not cached))
+                 (pgg-remove-passphrase-from-cache cache-id t))))
+        ;; confirmation vs new input - doing pgg-read-passphrase will do the
+        ;; right thing, in either case:
+        (if (not confirmation)
+            (setq confirmation
+                  (pgg-read-passphrase (concat prompt
+                                               " ... confirm spelling: ")
+                                       cache-id t)))
+        (prog1
+            (if (equal got-pass confirmation)
+                confirmation
+              (if (yes-or-no-p (concat "spelling of original and"
+                                       " confirmation differ - retry? "))
+                  (progn (setq retried (if retried (1+ retried) 1))
+                         (pgg-remove-passphrase-from-cache cache-id t)
+                         ;; recurse to this routine:
+                         (pgg-read-passphrase prompt-sans-hint cache-id t))
+                (pgg-remove-passphrase-from-cache cache-id t)
+                (error "Confirmation failed."))))))))
+;;;_  > allout-encrypted-topic-p ()
+(defun allout-encrypted-topic-p ()
+  "True if the current topic is encryptable and encrypted."
+  (save-excursion
+    (allout-end-of-prefix t)
+    (and (string= (buffer-substring-no-properties (1- (point)) (point))
+                  allout-topic-encryption-bullet)
+         (save-match-data (looking-at "\\*")))
+    )
+  )
+;;;_  > allout-encrypted-key-info (text)
+;; XXX gpg-specific, alas
+(defun allout-encrypted-key-info (text)
+  "Return a pair of the key type and identity of a recipient's secret key.
+
+The key type is one of 'symmetric or 'keypair.
+
+if 'keypair, and some of the user's secret keys are among those for which
+the message was encoded, return the identity of the first.  otherwise,
+return nil for the second item of the pair.
+
+An error is raised if the text is not encrypted."
+  (require 'pgg-parse)
+  (save-excursion
+    (with-temp-buffer
+      (insert text)
+      (let* ((parsed-armor (pgg-parse-armor-region (point-min) (point-max)))
+             (type (if (pgg-gpg-symmetric-key-p parsed-armor)
+                       'symmetric
+                     'keypair))
+             secret-keys first-secret-key for-key-owner)
+        (if (equal type 'keypair)
+            (setq secret-keys (pgg-gpg-lookup-all-secret-keys)
+                  first-secret-key (pgg-gpg-select-matching-key parsed-armor
+                                                                secret-keys)
+                  for-key-owner (and first-secret-key
+                                     (pgg-gpg-lookup-key-owner
+                                      first-secret-key))))
+        (list type (pgg-gpg-key-id-from-key-owner for-key-owner))
+        )
+      )
+    )
+  )
+;;;_  > allout-create-encryption-passphrase-verifier (passphrase)
+(defun allout-create-encryption-passphrase-verifier (passphrase)
+  "Encrypt random message for later validation of symmetric key's passphrase."
+  ;; use 20 random ascii characters, across the entire ascii range.
+  (random t)
+  (let ((spew (make-string 20 ?\0)))
+    (dotimes (i (length spew))
+      (aset spew i (1+ (random 254))))
+    (allout-encrypt-string spew nil (current-buffer) 'symmetric
+                           nil nil 0 0 passphrase))
+  )
+;;;_  > allout-update-passphrase-mnemonic-aids (for-key passphrase
+;;;                                                     outline-buffer)
+(defun allout-update-passphrase-mnemonic-aids (for-key passphrase
+                                                       outline-buffer)
+  "Update passphrase verifier and hint strings if necessary.
+
+See `allout-passphrase-verifier-string' and `allout-passphrase-hint-string'
+settings.
+
+PASSPHRASE is the passphrase being mnemonicized
+
+OUTLINE-BUFFER is the buffer of the outline being adjusted.
+
+These are used to help the user keep track of the passphrase they use for
+symmetric encryption in the file.
+
+Behavior is governed by `allout-passphrase-verifier-handling',
+`allout-passphrase-hint-handling', and also, controlling whether the values
+are preserved on Emacs local file variables,
+`allout-enable-file-variable-adjustment'."
+
+  ;; If passphrase doesn't agree with current verifier:
+  ;;   - adjust the verifier
+  ;;   - if passphrase hint handling is enabled, adjust the passphrase hint
+  ;;   - if file var settings are enabled, adjust the file vars
+
+  (let* ((new-verifier-needed (not (allout-verify-passphrase
+                                    for-key passphrase outline-buffer)))
+         (new-verifier-string
+          (if new-verifier-needed
+              ;; Collapse to a single line and enclose in string quotes:
+              (subst-char-in-string
+               ?\n ?\C-a (allout-create-encryption-passphrase-verifier
+                          passphrase))))
+         new-hint)
+    (when new-verifier-string
+      ;; do the passphrase hint first, since it's interactive
+      (when (and allout-passphrase-hint-handling
+                 (not (equal allout-passphrase-hint-handling 'disabled)))
+        (setq new-hint
+              (read-from-minibuffer "Passphrase hint to jog your memory: "
+                                    allout-passphrase-hint-string))
+        (when (not (string= new-hint allout-passphrase-hint-string))
+          (setq allout-passphrase-hint-string new-hint)
+          (allout-adjust-file-variable "allout-passphrase-hint-string"
+                                       allout-passphrase-hint-string)))
+      (when allout-passphrase-verifier-handling
+        (setq allout-passphrase-verifier-string new-verifier-string)
+        (allout-adjust-file-variable "allout-passphrase-verifier-string"
+                                     allout-passphrase-verifier-string))
+      )
+    )
+  )
+;;;_  > allout-get-encryption-passphrase-verifier ()
+(defun allout-get-encryption-passphrase-verifier ()
+  "Return text of the encrypt passphrase verifier, unmassaged, or nil if none.
+
+Derived from value of `allout-passphrase-verifier-string'."
+
+  (let ((verifier-string (and (boundp 'allout-passphrase-verifier-string)
+                              allout-passphrase-verifier-string)))
+    (if verifier-string
+        ;; Return it uncollapsed
+        (subst-char-in-string ?\C-a ?\n verifier-string))
+   )
+  )
+;;;_  > allout-verify-passphrase (key passphrase allout-buffer)
+(defun allout-verify-passphrase (key passphrase allout-buffer)
+  "True if passphrase successfully decrypts verifier, nil otherwise.
+
+\"Otherwise\" includes absence of passphrase verifier."
+  (save-excursion
+    (set-buffer allout-buffer)
+    (and (boundp 'allout-passphrase-verifier-string)
+         allout-passphrase-verifier-string
+         (allout-encrypt-string (allout-get-encryption-passphrase-verifier)
+                                 'decrypt allout-buffer 'symmetric
+                                 key nil 0 0 'verifying passphrase)
+         t)))
+;;;_  > allout-next-topic-pending-encryption (&optional except-mark)
+(defun allout-next-topic-pending-encryption (&optional except-mark)
+  "Return the point of the next topic pending encryption, or nil if none.
+
+EXCEPT-MARK identifies a point whose containing topics should be excluded
+from encryption.  This supports 'except-current mode of
+`allout-encrypt-unencrypted-on-saves'.
+
+Such a topic has the allout-topic-encryption-bullet without an
+immediately following '*' that would mark the topic as being encrypted.  It
+must also have content."
+  (let (done got content-beg)
+    (save-match-data
+      (while (not done)
+
+        (if (not (re-search-forward
+                  (format "\\(\\`\\|\n\\)%s *%s[^*]"
+                          (regexp-quote allout-header-prefix)
+                          (regexp-quote allout-topic-encryption-bullet))
+                  nil t))
+            (setq got nil
+                  done t)
+          (goto-char (setq got (match-beginning 0)))
+          (if (save-match-data (looking-at "\n"))
+              (forward-char 1))
+          (setq got (point)))
+
+        (cond ((not got)
+               (setq done t))
+
+              ((not (search-forward "\n"))
+               (setq got nil
+                     done t))
+
+              ((eobp)
+               (setq got nil
+                     done t))
+
+              (t
+               (setq content-beg (point))
+               (backward-char 1)
+               (allout-end-of-subtree)
+               (if (or (<= (point) content-beg)
+                       (and except-mark
+                            (<= content-beg except-mark)
+                            (>= (point) except-mark)))
+                   ;; Continue looking
+                   (setq got nil)
+                 ;; Got it!
+                 (setq done t)))
+              )
+        )
+      (if got
+          (goto-char got))
+      )
+    )
+  )
+;;;_  > allout-encrypt-decrypted (&optional except-mark)
+(defun allout-encrypt-decrypted (&optional except-mark)
+  "Encrypt topics pending encryption except those containing exemption point.
+
+EXCEPT-MARK identifies a point whose containing topics should be excluded
+from encryption.  This supports 'except-current mode of
+`allout-encrypt-unencrypted-on-saves'.
+
+If a topic that is currently being edited was encrypted, we return a list
+containing the location of the topic and the location of the cursor just
+before the topic was encrypted.  This can be used, eg, to decrypt the topic
+and exactly resituate the cursor if this is being done as part of a file
+save.  See `allout-encrypt-unencrypted-on-saves' for more info."
+
+  (interactive "p")
+  (save-match-data
+    (save-excursion
+      (let* ((current-mark (point-marker))
+             (current-mark-position (marker-position current-mark))
+             was-modified
+             bo-subtree
+             editing-topic editing-point)
+        (goto-char (point-min))
+        (while (allout-next-topic-pending-encryption except-mark)
+          (setq was-modified (buffer-modified-p))
+          (when (save-excursion
+                  (and (boundp 'allout-encrypt-unencrypted-on-saves)
+                       allout-encrypt-unencrypted-on-saves
+                       (setq bo-subtree (re-search-forward "$"))
+                       (not (allout-hidden-p))
+                       (>= current-mark (point))
+                       (allout-end-of-current-subtree)
+                       (<= current-mark (point))))
+            (setq editing-topic (point)
+                  ;; we had to wait for this 'til now so prior topics are
+                  ;; encrypted, any relevant text shifts are in place:
+                  editing-point (- current-mark-position
+                                   (count-trailing-whitespace-region
+                                    bo-subtree current-mark-position))))
+          (allout-toggle-subtree-encryption)
+          (if (not was-modified)
+              (set-buffer-modified-p nil))
+          )
+        (if (not was-modified)
+            (set-buffer-modified-p nil))
+        (if editing-topic (list editing-topic editing-point))
+        )
+      )
+    )
+  )
+
+;;;_ #9 miscellaneous
+;;;_  : Mode:
+;;;_   > outlineify-sticky ()
 ;; outlinify-sticky is correct spelling; provide this alias for sticklers:
+;;;###autoload
 (defalias 'outlinify-sticky 'outlineify-sticky)
+;;;###autoload
 (defun outlineify-sticky (&optional arg)
   "Activate outline mode and establish file var so it is started subsequently.
 
@@ -4534,27 +6594,137 @@ setup for auto-startup."
 
   (save-excursion
     (goto-char (point-min))
-    (if (looking-at allout-regexp)
+    (if (allout-goto-prefix)
        t
       (allout-open-topic 2)
       (insert (concat "Dummy outline topic header - see"
-                     "`allout-mode' docstring: `^Hm'."))
-      (forward-line 1)
+                      "`allout-mode' docstring: `^Hm'."))
+      (allout-adjust-file-variable
+       "allout-layout" (or allout-layout '(-1 : 0))))))
+;;;_   > allout-file-vars-section-data ()
+(defun allout-file-vars-section-data ()
+  "Return data identifying the file-vars section, or nil if none.
+
+Returns list `(beginning-point prefix-string suffix-string)'."
+  ;; minimally gleaned from emacs 21.4 files.el hack-local-variables function.
+  (let (beg prefix suffix)
+    (save-excursion
       (goto-char (point-max))
-      (open-line 1)
-      (allout-open-topic 0)
-      (insert "Local emacs vars.\n")
-      (allout-open-topic 1)
-      (insert "(`allout-layout' is for allout.el allout-mode)\n")
-      (allout-open-topic 0)
-      (insert "Local variables:\n")
-      (allout-open-topic 0)
-      (insert (format "allout-layout: %s\n"
-                            (or allout-layout
-                                '(-1 : 0))))
-      (allout-open-topic 0)
-      (insert "End:\n"))))
-;;;_  > solicit-char-in-string (prompt string &optional do-defaulting)
+      (search-backward "\n\^L" (max (- (point-max) 3000) (point-min)) 'move)
+      (if (let ((case-fold-search t))
+           (not (search-forward "Local Variables:" nil t)))
+          nil
+        (setq beg (- (point) 16))
+        (setq suffix (buffer-substring-no-properties
+                      (point)
+                      (progn (if (search-forward "\n" nil t)
+                                 (forward-char -1))
+                             (point))))
+        (setq prefix (buffer-substring-no-properties
+                      (progn (if (search-backward "\n" nil t)
+                                 (forward-char 1))
+                             (point))
+                      beg))
+        (list beg prefix suffix))
+      )
+    )
+  )
+;;;_   > allout-adjust-file-variable (varname value)
+(defun allout-adjust-file-variable (varname value)
+  "Adjust the setting of an Emacs file variable named VARNAME to VALUE.
+
+This activity is inhibited if either `enable-local-variables'
+`allout-enable-file-variable-adjustment' are nil.
+
+When enabled, an entry for the variable is created if not already present,
+or changed if established with a different value.  The section for the file
+variables, itself, is created if not already present.  When created, the
+section lines (including the section line) exist as second-level topics in
+a top-level topic at the end of the file.
+
+`enable-local-variables' must be true for any of this to happen."
+  (if (not (and enable-local-variables
+                allout-enable-file-variable-adjustment))
+      nil
+    (save-excursion
+      (let ((inhibit-field-text-motion t)
+            (section-data (allout-file-vars-section-data))
+            beg prefix suffix)
+        (if section-data
+            (setq beg (car section-data)
+                  prefix (cadr section-data)
+                  suffix (car (cddr section-data)))
+          ;; create the section
+          (goto-char (point-max))
+          (open-line 1)
+          (allout-open-topic 0)
+          (end-of-line)
+          (insert "Local emacs vars.\n")
+          (allout-open-topic 1)
+          (setq beg (point)
+                suffix ""
+                prefix (buffer-substring-no-properties (progn
+                                                         (beginning-of-line)
+                                                         (point))
+                                                       beg))
+          (goto-char beg)
+          (insert "Local variables:\n")
+          (allout-open-topic 0)
+          (insert "End:\n")
+          )
+        ;; look for existing entry or create one, leaving point for insertion
+        ;; of new value:
+        (goto-char beg)
+        (allout-show-to-offshoot)
+        (if (search-forward (concat "\n" prefix varname ":") nil t)
+            (let* ((value-beg (point))
+                   (line-end (progn (if (search-forward "\n" nil t)
+                                        (forward-char -1))
+                                    (point)))
+                   (value-end (- line-end (length suffix))))
+              (if (> value-end value-beg)
+                  (delete-region value-beg value-end)))
+          (end-of-line)
+          (open-line 1)
+          (forward-line 1)
+          (insert (concat prefix varname ":")))
+        (insert (format " %S%s" value suffix))
+        )
+      )
+    )
+  )
+;;;_   > allout-get-configvar-values (varname)
+(defun allout-get-configvar-values (configvar-name)
+  "Return a list of values of the symbols in list bound to CONFIGVAR-NAME.
+
+The user is prompted for removal of symbols that are unbound, and they
+otherwise are ignored.
+
+CONFIGVAR-NAME should be the name of the configuration variable,
+not its value."
+
+  (let ((configvar-value (symbol-value configvar-name))
+        got)
+    (dolist (sym configvar-value)
+      (if (not (boundp sym))
+          (if (yes-or-no-p (format "%s entry `%s' is unbound - remove it? "
+                                   configvar-name sym))
+              (delq sym (symbol-value configvar-name)))
+        (push (symbol-value sym) got)))
+    (reverse got)))
+;;;_  : Topics:
+;;;_   > allout-mark-topic ()
+(defun allout-mark-topic ()
+  "Put the region around topic currently containing point."
+  (interactive)
+  (let ((inhibit-field-text-motion t))
+    (beginning-of-line))
+  (allout-goto-prefix-doublechecked)
+  (push-mark (point))
+  (allout-end-of-current-subtree)
+  (exchange-point-and-mark))
+;;;_  : UI:
+;;;_   > solicit-char-in-string (prompt string &optional do-defaulting)
 (defun solicit-char-in-string (prompt string &optional do-defaulting)
   "Solicit (with first arg PROMPT) choice of a character from string STRING.
 
@@ -4587,15 +6757,15 @@ Optional arg DO-DEFAULTING indicates to accept empty input (CR)."
       ;; got something out of loop - return it:
       got)
   )
-;;;_  > regexp-sans-escapes (string)
+;;;_  : Strings:
+;;;_   > regexp-sans-escapes (string)
 (defun regexp-sans-escapes (regexp &optional successive-backslashes)
   "Return a copy of REGEXP with all character escapes stripped out.
 
 Representations of actual backslashes - '\\\\\\\\' - are left as a
 single backslash.
 
-\(fn REGEXP)"
-;; Optional arg SUCCESSIVE-BACKSLASHES is used internally for recursion.
+Optional arg SUCCESSIVE-BACKSLASHES is used internally for recursion."
 
   (if (string= regexp "")
       ""
@@ -4611,45 +6781,201 @@ single backslash.
                (regexp-sans-escapes (substring regexp 1)))
       ;; Exclude first char, but maintain count:
       (regexp-sans-escapes (substring regexp 1) successive-backslashes))))
-;;;_  > my-region-active-p ()
-(defmacro my-region-active-p ()
-  (if (fboundp 'region-active-p)
-      '(region-active-p)
-    'mark-active))
-;;;_  - add-hook definition for divergent emacsen
-;;;_   > add-hook (hook function &optional append)
-(if (not (fboundp 'add-hook))
-    (defun add-hook (hook function &optional append)
-      "Add to the value of HOOK the function FUNCTION unless already present.
-\(It becomes the first hook on the list unless optional APPEND is non-nil, in
-which case it becomes the last).  HOOK should be a symbol, and FUNCTION may be
-any valid function.  HOOK's value should be a list of functions, not a single
-function.  If HOOK is void, it is first set to nil."
-      (or (boundp hook) (set hook nil))
-      (or (if (consp function)
-             ;; Clever way to tell whether a given lambda-expression
-             ;; is equal to anything in the hook.
-             (let ((tail (assoc (cdr function) (symbol-value hook))))
-               (equal function tail))
-           (memq function (symbol-value hook)))
-         (set hook
-              (if append
-                  (nconc (symbol-value hook) (list function))
-                (cons function (symbol-value hook)))))))
-;;;_  : my-mark-marker to accommodate divergent emacsen:
-(defun my-mark-marker (&optional force buffer)
+;;;_   > count-trailing-whitespace-region (beg end)
+(defun count-trailing-whitespace-region (beg end)
+  "Return number of trailing whitespace chars between BEG and END.
+
+If BEG is bigger than END we return 0."
+  (if (> beg end)
+      0
+    (save-match-data
+      (save-excursion
+        (goto-char beg)
+        (let ((count 0))
+          (while (re-search-forward "[         ][      ]*$" end t)
+            (goto-char (1+ (match-beginning 2)))
+            (setq count (1+ count)))
+          count)))))
+;;;_   > allout-format-quote (string)
+(defun allout-format-quote (string)
+  "Return a copy of string with all \"%\" characters doubled."
+  (apply 'concat
+         (mapcar (lambda (char) (if (= char ?%) "%%" (char-to-string char)))
+                 string)))
+;;;_  : lists
+;;;_   > allout-flatten (list)
+(defun allout-flatten (list)
+  "Return a list of all atoms in list."
+  ;; classic.
+  (cond ((null list) nil)
+        ((atom (car list)) (cons (car list) (allout-flatten (cdr list))))
+        (t (append (allout-flatten (car list)) (allout-flatten (cdr list))))))
+;;;_  : Compatability:
+;;;_   > allout-mark-marker to accommodate divergent emacsen:
+(defun allout-mark-marker (&optional force buffer)
   "Accommodate the different signature for `mark-marker' across Emacsen.
 
-XEmacs takes two optional args, while GNU Emacs does not,
+XEmacs takes two optional args, while mainline GNU Emacs does not,
 so pass them along when appropriate."
   (if (featurep 'xemacs)
-      (mark-marker force buffer)
+      (apply 'mark-marker force buffer)
     (mark-marker)))
+;;;_   > subst-char-in-string if necessary
+(if (not (fboundp 'subst-char-in-string))
+    (defun subst-char-in-string (fromchar tochar string &optional inplace)
+      "Replace FROMCHAR with TOCHAR in STRING each time it occurs.
+Unless optional argument INPLACE is non-nil, return a new string."
+      (let ((i (length string))
+            (newstr (if inplace string (copy-sequence string))))
+        (while (> i 0)
+          (setq i (1- i))
+          (if (eq (aref newstr i) fromchar)
+              (aset newstr i tochar)))
+        newstr)))
+;;;_   > wholenump if necessary
+(if (not (fboundp 'wholenump))
+    (defalias 'wholenump 'natnump))
+;;;_   > remove-overlays if necessary
+(if (not (fboundp 'remove-overlays))
+    (defun remove-overlays (&optional beg end name val)
+      "Clear BEG and END of overlays whose property NAME has value VAL.
+Overlays might be moved and/or split.
+BEG and END default respectively to the beginning and end of buffer."
+      (unless beg (setq beg (point-min)))
+      (unless end (setq end (point-max)))
+      (if (< end beg)
+          (setq beg (prog1 end (setq end beg))))
+      (save-excursion
+        (dolist (o (overlays-in beg end))
+          (when (eq (overlay-get o name) val)
+            ;; Either push this overlay outside beg...end
+            ;; or split it to exclude beg...end
+            ;; or delete it entirely (if it is contained in beg...end).
+            (if (< (overlay-start o) beg)
+                (if (> (overlay-end o) end)
+                    (progn
+                      (move-overlay (copy-overlay o)
+                                    (overlay-start o) beg)
+                      (move-overlay o end (overlay-end o)))
+                  (move-overlay o (overlay-start o) beg))
+              (if (> (overlay-end o) end)
+                  (move-overlay o end (overlay-end o))
+                (delete-overlay o)))))))
+  )
+;;;_   > copy-overlay if necessary - xemacs ~ 21.4
+(if (not (fboundp 'copy-overlay))
+    (defun copy-overlay (o)
+      "Return a copy of overlay O."
+      (let ((o1 (make-overlay (overlay-start o) (overlay-end o)
+                              ;; FIXME: there's no easy way to find the
+                              ;; insertion-type of the two markers.
+                              (overlay-buffer o)))
+            (props (overlay-properties o)))
+        (while props
+          (overlay-put o1 (pop props) (pop props)))
+        o1)))
+;;;_   > add-to-invisibility-spec if necessary - xemacs ~ 21.4
+(if (not (fboundp 'add-to-invisibility-spec))
+    (defun add-to-invisibility-spec (element)
+      "Add ELEMENT to `buffer-invisibility-spec'.
+See documentation for `buffer-invisibility-spec' for the kind of elements
+that can be added."
+      (if (eq buffer-invisibility-spec t)
+          (setq buffer-invisibility-spec (list t)))
+      (setq buffer-invisibility-spec
+            (cons element buffer-invisibility-spec))))
+;;;_   > remove-from-invisibility-spec if necessary - xemacs ~ 21.4
+(if (not (fboundp 'remove-from-invisibility-spec))
+    (defun remove-from-invisibility-spec (element)
+      "Remove ELEMENT from `buffer-invisibility-spec'."
+      (if (consp buffer-invisibility-spec)
+          (setq buffer-invisibility-spec (delete element
+                                                 buffer-invisibility-spec)))))
+;;;_   > move-beginning-of-line if necessary - older emacs, xemacs
+(if (not (fboundp 'move-beginning-of-line))
+    (defun move-beginning-of-line (arg)
+      "Move point to beginning of current line as displayed.
+\(This disregards invisible newlines such as those
+which are part of the text that an image rests on.)
+
+With argument ARG not nil or 1, move forward ARG - 1 lines first.
+If point reaches the beginning or end of buffer, it stops there.
+To ignore intangibility, bind `inhibit-point-motion-hooks' to t."
+      (interactive "p")
+      (or arg (setq arg 1))
+      (if (/= arg 1)
+          (condition-case nil (line-move (1- arg)) (error nil)))
+
+      ;; Move to beginning-of-line, ignoring fields and invisibles.
+      (skip-chars-backward "^\n")
+      (while (and (not (bobp))
+                  (let ((prop
+                          (get-char-property (1- (point)) 'invisible)))
+                    (if (eq buffer-invisibility-spec t)
+                        prop
+                      (or (memq prop buffer-invisibility-spec)
+                          (assq prop buffer-invisibility-spec)))))
+        (goto-char (if (featurep 'xemacs)
+                       (previous-property-change (point))
+                     (previous-char-property-change (point))))
+        (skip-chars-backward "^\n"))
+      (vertical-motion 0))
+)
+;;;_   > move-end-of-line if necessary - older emacs, xemacs
+(if (not (fboundp 'move-end-of-line))
+    (defun move-end-of-line (arg)
+      "Move point to end of current line as displayed.
+\(This disregards invisible newlines such as those
+which are part of the text that an image rests on.)
+
+With argument ARG not nil or 1, move forward ARG - 1 lines first.
+If point reaches the beginning or end of buffer, it stops there.
+To ignore intangibility, bind `inhibit-point-motion-hooks' to t."
+      (interactive "p")
+      (or arg (setq arg 1))
+      (let (done)
+        (while (not done)
+          (let ((newpos
+                 (save-excursion
+                   (let ((goal-column 0))
+                     (and (condition-case nil
+                              (or (line-move arg) t)
+                            (error nil))
+                          (not (bobp))
+                          (progn
+                            (while
+                                (and
+                                 (not (bobp))
+                                 (let ((prop
+                                        (get-char-property (1- (point))
+                                                           'invisible)))
+                                   (if (eq buffer-invisibility-spec t)
+                                       prop
+                                     (or (memq prop
+                                               buffer-invisibility-spec)
+                                         (assq prop
+                                               buffer-invisibility-spec)))))
+                              (goto-char
+                               (previous-char-property-change (point))))
+                            (backward-char 1)))
+                     (point)))))
+            (goto-char newpos)
+            (if (and (> (point) newpos)
+                     (eq (preceding-char) ?\n))
+                (backward-char 1)
+              (if (and (> (point) newpos) (not (eobp))
+                       (not (eq (following-char) ?\n)))
+                  ;; If we skipped something intangible
+                  ;; and now we're not really at eol,
+                  ;; keep going.
+                  (setq arg 1)
+                (setq done t)))))))
+  )
 
-;;;_ #9 Under development
+;;;_ #10 Unfinished
 ;;;_  > allout-bullet-isearch (&optional bullet)
 (defun allout-bullet-isearch (&optional bullet)
-  "Isearch \(regexp) for topic with bullet BULLET."
+  "Isearch (regexp) for topic with bullet BULLET."
   (interactive)
   (if (not bullet)
       (setq bullet (solicit-char-in-string
@@ -4663,18 +6989,143 @@ so pass them along when appropriate."
                                bullet)))
     (isearch-repeat 'forward)
     (isearch-mode t)))
-;;;_  ? Re hooking up with isearch - use isearch-op-fun rather than
-;;;    wrapping the isearch functions.
 
-;;;_* Local emacs vars.
-;;; The following `allout-layout' local variable setting:
-;;;  - closes all topics from the first topic to just before the third-to-last,
-;;;  - shows the children of the third to last (config vars)
-;;;  - and the second to last (code section),
-;;;  - and closes the last topic (this local-variables section).
-;;;Local variables:
-;;;allout-layout: (0 : -1 -1 0)
-;;;End:
+;;;_ #11 Unit tests - this should be last item before "Provide"
+;;;_  > allout-run-unit-tests ()
+(defun allout-run-unit-tests ()
+  "Run the various allout unit tests."
+  (message "Running allout tests...")
+  (allout-test-resumptions)
+  (message "Running allout tests...  Done.")
+  (sit-for .5))
+;;;_  : test resumptions:
+;;;_   > allout-tests-obliterate-variable (name)
+(defun allout-tests-obliterate-variable (name)
+  "Completely unbind variable with NAME."
+  (if (local-variable-p name) (kill-local-variable name))
+  (while (boundp name) (makunbound name)))
+;;;_   > allout-test-resumptions ()
+(defvar allout-tests-globally-unbound nil
+  "Fodder for allout resumptions tests - defvar just for byte compiler.")
+(defvar allout-tests-globally-true nil
+  "Fodder for allout resumptions tests - defvar just just for byte compiler.")
+(defvar allout-tests-locally-true nil
+  "Fodder for allout resumptions tests - defvar just for byte compiler.")
+(defun allout-test-resumptions ()
+  "Exercise allout resumptions."
+  ;; for each resumption case, we also test that the right local/global
+  ;; scopes are affected during resumption effects:
+
+  ;; ensure that previously unbound variables return to the unbound state.
+  (with-temp-buffer
+    (allout-tests-obliterate-variable 'allout-tests-globally-unbound)
+    (allout-add-resumptions '(allout-tests-globally-unbound t))
+    (assert (not (default-boundp 'allout-tests-globally-unbound)))
+    (assert (local-variable-p 'allout-tests-globally-unbound))
+    (assert (boundp 'allout-tests-globally-unbound))
+    (assert (equal allout-tests-globally-unbound t))
+    (allout-do-resumptions)
+    (assert (not (local-variable-p 'allout-tests-globally-unbound)))
+    (assert (not (boundp 'allout-tests-globally-unbound))))
+
+  ;; ensure that variable with prior global value is resumed
+  (with-temp-buffer
+    (allout-tests-obliterate-variable 'allout-tests-globally-true)
+    (setq allout-tests-globally-true t)
+    (allout-add-resumptions '(allout-tests-globally-true nil))
+    (assert (equal (default-value 'allout-tests-globally-true) t))
+    (assert (local-variable-p 'allout-tests-globally-true))
+    (assert (equal allout-tests-globally-true nil))
+    (allout-do-resumptions)
+    (assert (not (local-variable-p 'allout-tests-globally-true)))
+    (assert (boundp 'allout-tests-globally-true))
+    (assert (equal allout-tests-globally-true t)))
+
+  ;; ensure that prior local value is resumed
+  (with-temp-buffer
+    (allout-tests-obliterate-variable 'allout-tests-locally-true)
+    (set (make-local-variable 'allout-tests-locally-true) t)
+    (assert (not (default-boundp 'allout-tests-locally-true))
+            nil (concat "Test setup mistake - variable supposed to"
+                        " not have global binding, but it does."))
+    (assert (local-variable-p 'allout-tests-locally-true)
+            nil (concat "Test setup mistake - variable supposed to have"
+                        " local binding, but it lacks one."))
+    (allout-add-resumptions '(allout-tests-locally-true nil))
+    (assert (not (default-boundp 'allout-tests-locally-true)))
+    (assert (local-variable-p 'allout-tests-locally-true))
+    (assert (equal allout-tests-locally-true nil))
+    (allout-do-resumptions)
+    (assert (boundp 'allout-tests-locally-true))
+    (assert (local-variable-p 'allout-tests-locally-true))
+    (assert (equal allout-tests-locally-true t))
+    (assert (not (default-boundp 'allout-tests-locally-true))))
+
+  ;; ensure that last of multiple resumptions holds, for various scopes.
+  (with-temp-buffer
+    (allout-tests-obliterate-variable 'allout-tests-globally-unbound)
+    (allout-tests-obliterate-variable 'allout-tests-globally-true)
+    (setq allout-tests-globally-true t)
+    (allout-tests-obliterate-variable 'allout-tests-locally-true)
+    (set (make-local-variable 'allout-tests-locally-true) t)
+    (allout-add-resumptions '(allout-tests-globally-unbound t)
+                            '(allout-tests-globally-true nil)
+                            '(allout-tests-locally-true nil))
+    (allout-add-resumptions '(allout-tests-globally-unbound 2)
+                            '(allout-tests-globally-true 3)
+                            '(allout-tests-locally-true 4))
+    ;; reestablish many of the basic conditions are maintained after re-add:
+    (assert (not (default-boundp 'allout-tests-globally-unbound)))
+    (assert (local-variable-p 'allout-tests-globally-unbound))
+    (assert (equal allout-tests-globally-unbound 2))
+    (assert (default-boundp 'allout-tests-globally-true))
+    (assert (local-variable-p 'allout-tests-globally-true))
+    (assert (equal allout-tests-globally-true 3))
+    (assert (not (default-boundp 'allout-tests-locally-true)))
+    (assert (local-variable-p 'allout-tests-locally-true))
+    (assert (equal allout-tests-locally-true 4))
+    (allout-do-resumptions)
+    (assert (not (local-variable-p 'allout-tests-globally-unbound)))
+    (assert (not (boundp 'allout-tests-globally-unbound)))
+    (assert (not (local-variable-p 'allout-tests-globally-true)))
+    (assert (boundp 'allout-tests-globally-true))
+    (assert (equal allout-tests-globally-true t))
+    (assert (boundp 'allout-tests-locally-true))
+    (assert (local-variable-p 'allout-tests-locally-true))
+    (assert (equal allout-tests-locally-true t))
+    (assert (not (default-boundp 'allout-tests-locally-true))))
+
+  ;; ensure that deliberately unbinding registered variables doesn't foul things
+  (with-temp-buffer
+    (allout-tests-obliterate-variable 'allout-tests-globally-unbound)
+    (allout-tests-obliterate-variable 'allout-tests-globally-true)
+    (setq allout-tests-globally-true t)
+    (allout-tests-obliterate-variable 'allout-tests-locally-true)
+    (set (make-local-variable 'allout-tests-locally-true) t)
+    (allout-add-resumptions '(allout-tests-globally-unbound t)
+                            '(allout-tests-globally-true nil)
+                            '(allout-tests-locally-true nil))
+    (allout-tests-obliterate-variable 'allout-tests-globally-unbound)
+    (allout-tests-obliterate-variable 'allout-tests-globally-true)
+    (allout-tests-obliterate-variable 'allout-tests-locally-true)
+    (allout-do-resumptions))
+  )
+;;;_  % Run unit tests if `allout-run-unit-tests-after-load' is true:
+(when allout-run-unit-tests-on-load
+  (allout-run-unit-tests))
+
+;;;_ #12 Provide
+(provide 'allout)
 
-;;; arch-tag: cf38fbc3-c044-450f-8bff-afed8ba5681c
+;;;_* Local emacs vars.
+;; The following `allout-layout' local variable setting:
+;;  - closes all topics from the first topic to just before the third-to-last,
+;;  - shows the children of the third to last (config vars)
+;;  - and the second to last (code section),
+;;  - and closes the last topic (this local-variables section).
+;;Local variables:
+;;allout-layout: (0 : -1 -1 0)
+;;End:
+
+;; arch-tag: cf38fbc3-c044-450f-8bff-afed8ba5681c
 ;;; allout.el ends here