(list-colors-display): Use with-help-window instead of with-output-to-temp-buffer.
[bpt/emacs.git] / lisp / view.el
index 6c5b744..ad6ca93 100644 (file)
@@ -1,15 +1,17 @@
-;;; view.el --- peruse file or buffer without editing.
+;;; view.el --- peruse file or buffer without editing
 
-;; Copyright (C) 1985, 1989, 1994, 1995, 1997 Free Software Foundation, Inc.
+;; Copyright (C) 1985, 1989, 1994, 1995, 1997, 2000, 2001, 2002,
+;;   2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
 
 ;; Author: K. Shane Hartman
 ;; Maintainer: Inge Frick <inge@nada.kth.se>
+;; Keywords: files
 
 ;; 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,
@@ -19,8 +21,8 @@
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
@@ -30,7 +32,7 @@
 ;; and view-mode-exit.  Use these functions to enter or exit view-mode from
 ;; emacs lisp programs.
 ;; We use both view- and View- as prefix for symbols.  View- is used as
-;; prefix for commands that have a key binding. view- is used for commands
+;; prefix for commands that have a key binding.  view- is used for commands
 ;; without key binding.  The purpose of this is to make it easier for a
 ;; user to use command name completion.
 
 ;;
 ;; You could also bind view-file, view-buffer, view-buffer-other-window and
 ;; view-buffer-other-frame to keys.
-
+\f
 ;;; Code:
 
-;;;###autoload
-(defvar view-highlight-face 'highlight
-   "*The face used for highlighting the match found by View mode search.")
+(defgroup view nil
+  "Peruse file or buffer without editing."
+  :link '(function-link view-mode)
+  :link '(custom-manual "(emacs)Misc File Ops")
+  :group 'wp
+  :group 'editing)
+
+(defcustom view-highlight-face 'highlight
+   "*The face used for highlighting the match found by View mode search."
+   :type 'face
+   :group 'view)
 
 ;; `view-mode-auto-exit' is replaced by the following option variable which
 ;; only says if scrolling past buffer end should leave view mode or not, it
 ;; doesn't say if leaving view mode should restore windows or not.  The latter
 ;; is now controlled by the presence of a value in `view-return-to-alist'.
-;;;###autoload
-(defvar view-scroll-auto-exit nil
+(defcustom view-scroll-auto-exit nil
   "*Non-nil means scrolling past the end of buffer exits View mode.
-nil means attempting to scroll past the end of the buffer,
-only rings the bell and gives a message on how to leave.")
+A value of nil means attempting to scroll past the end of the buffer,
+only rings the bell and gives a message on how to leave."
+  :type 'boolean
+  :group 'view)
 
-;;;###autoload
-(defvar view-try-extend-at-buffer-end nil
- "*Non-nil means try load more of file when reaching end of buffer.
+(defcustom view-try-extend-at-buffer-end nil
+ "*Non-nil means try to load more of file when reaching end of buffer.
 This variable is mainly intended to be temporarily set to non-nil by
 the F command in view-mode, but you can set it to t if you want the action
-for all scroll commands in view mode.")
-
-(defvar view-remove-frame-by-deleting nil
-  "*Determine how to View mode removes a frame no longer needed.
-If nil, make an icon of the frame.  If non-nil, delete the frame.")
+for all scroll commands in view mode."
+  :type 'boolean
+  :group 'view)
 
 ;;;###autoload
-(defvar view-exits-all-viewing-windows nil
+(defcustom view-remove-frame-by-deleting t
+  "*Determine how View mode removes a frame no longer needed.
+If nil, make an icon of the frame.  If non-nil, delete the frame."
+  :type 'boolean
+  :group 'view
+  ;; Changed the default of this to t for Emacs 23.  Users consider
+  ;; frame iconification annoying.
+  :version "23.1")
+
+(defcustom view-exits-all-viewing-windows nil
   "*Non-nil means restore all windows used to view buffer.
 Commands that restore windows when finished viewing a buffer, apply to all
 windows that display the buffer and have restore information in
-`view-return-to-alist'.")
+`view-return-to-alist'.
+If `view-exits-all-viewing-windows' is nil, only the selected window is
+considered for restoring."
+  :type 'boolean
+  :group 'view)
+
+(defcustom view-inhibit-help-message nil
+  "*Non-nil inhibits the help message shown upon entering View mode."
+  :type 'boolean
+  :group 'view
+  :version "22.1")
 
 ;;;###autoload
-(defvar view-mode nil "Non-nil if View mode is enabled.")
+(defvar view-mode nil
+  "Non-nil if View mode is enabled.
+Don't change this variable directly, you must change it by one of the
+functions that enable or disable view mode.")
 ;;;###autoload
 (make-variable-buffer-local 'view-mode)
 
-(defvar view-mode-hook nil
-  "Normal hook run when starting to view a buffer or file.")
-
+(defcustom view-mode-hook nil
+  "Normal hook run when starting to view a buffer or file."
+  :type 'hook
+  :group 'view)
+\f
 (defvar view-old-buffer-read-only nil)
 (make-variable-buffer-local 'view-old-buffer-read-only)
 
 (defvar view-old-Helper-return-blurb)
 (make-variable-buffer-local 'view-old-Helper-return-blurb)
 
+;; Just to avoid warnings.
+(defvar Helper-return-blurb)
+
 (defvar view-page-size nil
   "Default number of lines to scroll by View page commands.
-If nil then the local value of this is initially set to window size.")
+If nil that means use the window size.")
 (make-variable-buffer-local 'view-page-size)
 
 (defvar view-half-page-size nil
   "Default number of lines to scroll by View half page commands.
-If nil then the local value of this is initially set to half window size.")
+If nil that means use half the window size.")
 (make-variable-buffer-local 'view-half-page-size)
 
 (defvar view-last-regexp nil)
@@ -106,18 +141,20 @@ If nil then the local value of this is initially set to half window size.")
 (defvar view-return-to-alist nil
   "What to do with used windows and where to go when finished viewing buffer.
 This is local in each buffer being viewed.
-It is added to by view-mode-enter when starting to view a buffer and
-subtracted from by view-mode-exit when finished viewing the buffer.
+It is added to by `view-mode-enter' when starting to view a buffer and
+subtracted from by `view-mode-exit' when finished viewing the buffer.
 
 See RETURN-TO-ALIST argument of function `view-mode-exit' for the format of
 `view-return-to-alist'.")
 (make-variable-buffer-local 'view-return-to-alist)
+(put 'view-return-to-alist 'permanent-local t)
 
 (defvar view-exit-action nil
-  "nil or a function with one argument (a buffer) called when finished viewing.
-This is local in each buffer being viewed.
-The \\[view-file] and \\[view-file-other-window] commands may set this to
-`kill-buffer'.")
+  "If non-nil, a function with one argument (a buffer) called when finished viewing.
+Commands like \\[view-file] and \\[view-file-other-window] may
+set this to bury or kill the viewed buffer.
+Observe that the buffer viewed might not appear in any window at
+the time this function is called.")
 (make-variable-buffer-local 'view-exit-action)
 
 (defvar view-no-disable-on-exit nil
@@ -131,11 +168,16 @@ that use View mode automatically.")
 This is local in each buffer, once it is used.")
 (make-variable-buffer-local 'view-overlay)
 
-(or (assq 'view-mode minor-mode-alist)
-    (setq minor-mode-alist
-         (cons '(view-mode " View") minor-mode-alist)))
-
+(unless (assq 'view-mode minor-mode-alist)
+  (setq minor-mode-alist
+       (cons (list 'view-mode
+                   (propertize " View"
+                               'local-map mode-line-minor-mode-keymap
+                               'help-echo "mouse-3: minor mode menu"))
+             minor-mode-alist)))
+\f
 ;; Define keymap inside defvar to make it easier to load changes.
+;; Some redundant "less"-like key bindings below have been commented out.
 (defvar view-mode-map
   (let ((map (make-sparse-keymap)))
     (define-key map "C" 'View-kill-and-leave)
@@ -156,7 +198,7 @@ This is local in each buffer, once it is used.")
     (define-key map "m" 'point-to-register)
     (define-key map "'" 'register-to-point)
     (define-key map "x" 'exchange-point-and-mark)
-    (define-key map "@" 'View-back-to-mark)  
+    (define-key map "@" 'View-back-to-mark)
     (define-key map "." 'set-mark-command)
     (define-key map "%" 'View-goto-percent)
 ;    (define-key map "G" 'View-goto-line-last)
@@ -191,101 +233,108 @@ This is local in each buffer, once it is used.")
     (define-key map "1" 'digit-argument)
     (define-key map "0" 'digit-argument)
     (define-key map "H" 'describe-mode)
-    (define-key map "?" 'describe-mode)        ; Maybe do as less instead?
+    (define-key map "?" 'describe-mode)        ; Maybe do as less instead? See above.
     (define-key map "h" 'describe-mode)
     map))
 
 (or (assq 'view-mode minor-mode-map-alist)
     (setq minor-mode-map-alist
          (cons (cons 'view-mode view-mode-map) minor-mode-map-alist)))
-
+\f
 ;;; Commands that enter or exit view mode.
 
 ;;;###autoload
-(defun view-file (file-name)
+(defun view-file (file)
   "View FILE in View mode, returning to previous buffer when done.
-Emacs commands editing the buffer contents are not available; instead,
-a special set of commands (mostly letters and punctuation)
-are defined for moving around in the buffer.
+Emacs commands editing the buffer contents are not available; instead, a
+special set of commands (mostly letters and punctuation) are defined for
+moving around in the buffer.
 Space scrolls forward, Delete scrolls backward.
-For list of all View commands, type H or h while viewing.
+For list of all View commands, type H or h while viewing.
 
 This command runs the normal hook `view-mode-hook'."
   (interactive "fView file: ")
-  (let ((had-a-buf (get-file-buffer file-name)))
-    (view-buffer (find-file-noselect file-name)
-                (and (not had-a-buf) 'kill-buffer))))
+  (unless (file-exists-p file) (error "%s does not exist" file))
+  (let ((had-a-buf (get-file-buffer file))
+       (buffer (find-file-noselect file)))
+    (if (eq (with-current-buffer buffer
+             (get major-mode 'mode-class))
+           'special)
+       (progn
+         (switch-to-buffer buffer)
+         (message "Not using View mode because the major mode is special"))
+      (view-buffer buffer (and (not had-a-buf) 'kill-buffer)))))
 
 ;;;###autoload
-(defun view-file-other-window (file-name)
+(defun view-file-other-window (file)
   "View FILE in View mode in another window.
-Return that window to its previous buffer when done.
-Emacs commands editing the buffer contents are not available; instead,
-a special set of commands (mostly letters and punctuation)
-are defined for moving around in the buffer.
+Return that window to its previous buffer when done.  Emacs commands
+editing the buffer contents are not available; instead, a special set of
+commands (mostly letters and punctuation) are defined for moving around
+in the buffer.
 Space scrolls forward, Delete scrolls backward.
-For list of all View commands, type H or h while viewing.
+For list of all View commands, type H or h while viewing.
 
 This command runs the normal hook `view-mode-hook'."
   (interactive "fIn other window view file: ")
-  (let ((had-a-buf (get-file-buffer file-name)))
-    (view-buffer-other-window (find-file-noselect file-name) nil
+  (unless (file-exists-p file) (error "%s does not exist" file))
+  (let ((had-a-buf (get-file-buffer file)))
+    (view-buffer-other-window (find-file-noselect file) nil
                              (and (not had-a-buf) 'kill-buffer))))
 
 ;;;###autoload
-(defun view-file-other-frame (file-name)
+(defun view-file-other-frame (file)
   "View FILE in View mode in another frame.
 Maybe delete other frame and/or return to previous buffer when done.
-Emacs commands editing the buffer contents are not available; instead,
-a special set of commands (mostly letters and punctuation)
-are defined for moving around in the buffer.
+Emacs commands editing the buffer contents are not available; instead, a
+special set of commands (mostly letters and punctuation) are defined for
+moving around in the buffer.
 Space scrolls forward, Delete scrolls backward.
-For list of all View commands, type H or h while viewing.
+For list of all View commands, type H or h while viewing.
 
 This command runs the normal hook `view-mode-hook'."
   (interactive "fIn other frame view file: ")
-  (let ((had-a-buf (get-file-buffer file-name)))
-    (view-buffer-other-frame (find-file-noselect file-name) nil
+  (unless (file-exists-p file) (error "%s does not exist" file))
+  (let ((had-a-buf (get-file-buffer file)))
+    (view-buffer-other-frame (find-file-noselect file) nil
                             (and (not had-a-buf) 'kill-buffer))))
 
 
 ;;;###autoload
-(defun view-buffer (buffer-name &optional exit-action)
+(defun view-buffer (buffer &optional exit-action)
   "View BUFFER in View mode, returning to previous buffer when done.
-Emacs commands editing the buffer contents are not available; instead,
-a special set of commands (mostly letters and punctuation)
-are defined for moving around in the buffer.
+Emacs commands editing the buffer contents are not available; instead, a
+special set of commands (mostly letters and punctuation) are defined for
+moving around in the buffer.
 Space scrolls forward, Delete scrolls backward.
-For list of all View commands, type H or h while viewing.
+For list of all View commands, type H or h while viewing.
 
 This command runs the normal hook `view-mode-hook'.
 
 Optional argument EXIT-ACTION is either nil or a function with buffer as
-argument.  This function is called when finished viewing buffer.
-Use this argument instead of explicitly setting `view-exit-action'."
-
+argument.  This function is called when finished viewing buffer.  Use
+this argument instead of explicitly setting `view-exit-action'."
   (interactive "bView buffer: ")
   (let ((undo-window (list (window-buffer) (window-start) (window-point))))
-    (switch-to-buffer buffer-name)
+    (switch-to-buffer buffer)
     (view-mode-enter (cons (selected-window) (cons nil undo-window))
                     exit-action)))
 
 ;;;###autoload
-(defun view-buffer-other-window
-  (buffer-name &optional not-return exit-action)
+(defun view-buffer-other-window (buffer &optional not-return exit-action)
   "View BUFFER in View mode in another window.
-Return to previous buffer when done, unless optional NOT-RETURN is non-nil.
-Emacs commands editing the buffer contents are not available; instead,
-a special set of commands (mostly letters and punctuation)
-are defined for moving around in the buffer.
+Return to previous buffer when done, unless optional NOT-RETURN is
+non-nil.  Emacs commands editing the buffer contents are not available;
+instead, a special set of commands (mostly letters and punctuation) are
+defined for moving around in the buffer.
 Space scrolls forward, Delete scrolls backward.
-For list of all View commands, type H or h while viewing.
+For list of all View commands, type H or h while viewing.
 
 This command runs the normal hook `view-mode-hook'.
 
 Optional argument EXIT-ACTION is either nil or a function with buffer as
-argument.  This function is called when finished viewing buffer.
-Use this argument instead of explicitly setting `view-exit-action'."
+argument.  This function is called when finished viewing buffer.  Use
+this argument instead of explicitly setting `view-exit-action'."
   (interactive "bIn other window view buffer:\nP")
   (let* ((win                          ; This window will be selected by
          (get-lru-window))             ; switch-to-buffer-other-window below.
@@ -294,44 +343,44 @@ Use this argument instead of explicitly setting `view-exit-action'."
               (cons (selected-window)
                     (if (eq win (selected-window))
                         t                      ; Has to make new window.
-                      (list 
+                      (list
                        (window-buffer win)     ; Other windows old buffer.
                        (window-start win)
                        (window-point win)))))))
-    (switch-to-buffer-other-window buffer-name)
+    (switch-to-buffer-other-window buffer)
     (view-mode-enter (and return-to (cons (selected-window) return-to))
                     exit-action)))
 
 ;;;###autoload
-(defun view-buffer-other-frame
-  (buffer-name &optional not-return exit-action)
+(defun view-buffer-other-frame (buffer &optional not-return exit-action)
   "View BUFFER in View mode in another frame.
-Return to previous buffer when done, unless optional NOT-RETURN is non-nil.
-Emacs commands editing the buffer contents are not available; instead,
-a special set of commands (mostly letters and punctuation)
-are defined for moving around in the buffer.
+Return to previous buffer when done, unless optional NOT-RETURN is
+non-nil.  Emacs commands editing the buffer contents are not available;
+instead, a special set of commands (mostly letters and punctuation) are
+defined for moving around in the buffer.
 Space scrolls forward, Delete scrolls backward.
-For list of all View commands, type H or h while viewing.
+For list of all View commands, type H or h while viewing.
 
 This command runs the normal hook `view-mode-hook'.
 
 Optional argument EXIT-ACTION is either nil or a function with buffer as
-argument.  This function is called when finished viewing buffer.
-Use this argument instead of explicitly setting `view-exit-action'."
+argument.  This function is called when finished viewing buffer.  Use
+this argument instead of explicitly setting `view-exit-action'."
   (interactive "bView buffer in other frame: \nP")
   (let ((return-to
         (and (not not-return) (cons (selected-window) t)))) ; Old window.
-    (switch-to-buffer-other-frame buffer-name)
+    (switch-to-buffer-other-frame buffer)
     (view-mode-enter (and return-to (cons (selected-window) return-to))
                     exit-action)))
-
+\f
 ;;;###autoload
 (defun view-mode (&optional arg)
   ;; In the following documentation string we have to use some explicit key
   ;; bindings instead of using the \\[] construction.  The reason for this
   ;; is that most commands have more than one key binding.
   "Toggle View mode, a minor mode for viewing text but not editing it.
-With arg, turn View mode on iff arg is positive.
+With prefix argument ARG, turn View mode on if ARG is positive, otherwise
+turn it off.
 
 Emacs commands that do not change the buffer contents are available as usual.
 Kill commands insert text in kill buffers but do not delete.  Other commands
@@ -350,14 +399,18 @@ Digits    provide prefix arguments.
 \\[beginning-of-buffer]        move to the beginning of buffer.
 >      move to the end of buffer.
 \\[View-scroll-to-buffer-end]  scroll so that buffer end is at last line of window.
-SPC    scroll forward prefix (default \"page size\") lines.
-DEL    scroll backward prefix (default \"page size\") lines.
-\\[View-scroll-page-forward-set-page-size]     like  \\[View-scroll-page-forward]  except prefix sets \"page size\".
-\\[View-scroll-page-backward-set-page-size]    like  \\[View-scroll-page-backward]  except prefix sets \"page size\".
-\\[View-scroll-half-page-forward]      scroll forward (and if prefix set) \"half page size\" lines.
-\\[View-scroll-half-page-backward]     scroll backward (and if prefix set) \"half page size\" lines.
-RET, LFD  scroll forward prefix (default one) line(s).
-y      scroll backward prefix (default one) line(s).
+SPC    scroll forward \"page size\" lines.
+         With prefix scroll forward prefix lines.
+DEL    scroll backward \"page size\" lines.
+         With prefix scroll backward prefix lines.
+\\[View-scroll-page-forward-set-page-size]     like  \\[View-scroll-page-forward]  but with prefix sets \"page size\" to prefix.
+\\[View-scroll-page-backward-set-page-size]    like  \\[View-scroll-page-backward]  but with prefix sets \"page size\" to prefix.
+\\[View-scroll-half-page-forward]      scroll forward \"half page size\" lines.  With prefix, sets
+         \"half page size\" to prefix lines and scrolls forward that much.
+\\[View-scroll-half-page-backward]     scroll backward \"half page size\" lines.  With prefix, sets
+         \"half page size\" to prefix lines and scrolls backward that much.
+RET, LFD  scroll forward one line.  With prefix scroll forward prefix line(s).
+y      scroll backward one line.  With prefix scroll backward prefix line(s).
 \\[View-revert-buffer-scroll-page-forward]     revert-buffer if necessary and scroll forward.
          Use this to view a changing file.
 \\[what-line]  prints the current line number.
@@ -379,41 +432,42 @@ r do reverse incremental search.
 \\     searches backward for regular expression, starting before current page.
 \\[View-search-last-regexp-forward]    searches forward for last regular expression.
 p      searches backward for last regular expression.
-\\[View-quit]  quit View mode, trying to restore window and buffer to previous state.
+\\[View-quit]  quit View mode, restoring this window and buffer to previous state.
          \\[View-quit] is the normal way to leave view mode.
 \\[View-exit]  exit View mode but stay in current buffer.  Use this if you started
          viewing a buffer (file) and find out you want to edit it.
-\\[View-exit-and-edit] exit View mode and make the current buffer editable.
-\\[View-quit-all]      quit View mode, trying to restore windows and buffer to previous state.
+         This command restores the previous read-only status of the buffer.
+\\[View-exit-and-edit] exit View mode, and make the current buffer editable
+         even if it was not editable before entry to View mode.
+\\[View-quit-all]      quit View mode, restoring all windows to previous state.
 \\[View-leave] quit View mode and maybe switch buffers, but don't kill this buffer.
 \\[View-kill-and-leave]        quit View mode, kill current buffer and go back to other buffer.
 
-The effect of \\[View-leave] , \\[View-quit] and \\[View-kill-and-leave] depends on how view-mode was entered.  If it was
-entered by view-file, view-file-other-window or view-file-other-frame (\\[view-file],
-\\[view-file-other-window], \\[view-file-other-frame] or the dired mode v command), then \\[View-quit] will try to kill the
-current buffer.  If view-mode was entered from another buffer as is done by
-View-buffer, View-buffer-other-window, View-buffer-other frame, View-file,
-View-file-other-window or View-file-other-frame then \\[view-leave] , \\[view-quit] and \\[view-kill-and-leave] will return
-to that buffer.
+The effect of \\[View-leave], \\[View-quit] and \\[View-kill-and-leave] depends on how view-mode was entered.  If it was
+entered by view-file, view-file-other-window, view-file-other-frame, or
+\\[dired-view-file] \(\\[view-file], \\[view-file-other-window],
+\\[view-file-other-frame], or the Dired mode v command),
+then \\[View-quit] will try to kill the current buffer.
+If view-mode was entered from another buffer, by \\[view-buffer],
+\\[view-buffer-other-window], \\[view-buffer-other frame], \\[view-file],
+\\[view-file-other-window], or \\[view-file-other-frame],
+then \\[View-leave], \\[View-quit] and \\[View-kill-and-leave] will return to that buffer.
 
 Entry to view-mode runs the normal hook `view-mode-hook'."
   (interactive "P")
-  (cond
-   ((and arg
-        (if (> (prefix-numeric-value arg) 0) view-mode (not view-mode)))
-    ())                                        ; Do nothing if already OK.
-   (view-mode (view-mode-disable))
-   (t (view-mode-enable))))
-
+  (unless (and arg                     ; Do nothing if already OK.
+              (if (> (prefix-numeric-value arg) 0) view-mode (not view-mode)))
+    (if view-mode (view-mode-disable)
+      (view-mode-enable))))
+\f
 (defun view-mode-enable ()
   "Turn on View mode."
   ;; Always leave view mode before changing major mode.
   ;; This is to guarantee that the buffer-read-only variable is restored.
-  (make-local-hook 'change-major-mode-hook)
   (add-hook 'change-major-mode-hook 'view-mode-disable nil t)
   (setq view-mode t
-       view-page-size (view-page-size-default view-page-size)
-       view-half-page-size (or view-half-page-size (/ (view-window-size) 2))
+       view-page-size nil
+       view-half-page-size nil
        view-old-buffer-read-only buffer-read-only
        buffer-read-only t
        view-old-Helper-return-blurb (and (boundp 'Helper-return-blurb)
@@ -423,127 +477,205 @@ Entry to view-mode runs the normal hook `view-mode-hook'."
                (if (buffer-file-name)
                    (file-name-nondirectory (buffer-file-name))
                  (buffer-name))))
+  (force-mode-line-update)
   (run-hooks 'view-mode-hook))
 
 (defun view-mode-disable ()
   "Turn off View mode."
   (remove-hook 'change-major-mode-hook 'view-mode-disable t)
   (and view-overlay (delete-overlay view-overlay))
+  (force-mode-line-update)
+  ;; Calling toggle-read-only while View mode is enabled
+  ;; sets view-read-only to t as a buffer-local variable
+  ;; after exiting View mode.  That arranges that the next toggle-read-only
+  ;; will reenable View mode.
+  ;; Cancelling View mode in any other way should cancel that, too,
+  ;; so that View mode stays off if toggle-read-only is called.
+  (if (local-variable-p 'view-read-only)
+      (kill-local-variable 'view-read-only))
   (setq view-mode nil
-       Helper-return-blurb view-old-Helper-return-blurb
-       buffer-read-only view-old-buffer-read-only))
+       Helper-return-blurb view-old-Helper-return-blurb)
+  (if buffer-read-only
+      (setq buffer-read-only view-old-buffer-read-only)))
+
+;;;###autoload
+(defun view-return-to-alist-update (buffer &optional item)
+  "Update `view-return-to-alist' of buffer BUFFER.
+Remove from `view-return-to-alist' all entries referencing dead
+windows.  Optional argument ITEM non-nil means add ITEM to
+`view-return-to-alist' after purging.  For a decsription of items
+that can be added see the RETURN-TO-ALIST argument of the
+function `view-mode-exit'.  If `view-return-to-alist' contains an
+entry for the selected window, purge that entry from
+`view-return-to-alist' before adding ITEM."
+  (with-current-buffer buffer
+    (when view-return-to-alist
+      (let* ((list view-return-to-alist)
+            entry entry-window last)
+       (while list
+         (setq entry (car list))
+         (setq entry-window (car entry))
+         (if (and (windowp entry-window)
+                  (or (and item (eq entry-window (selected-window)))
+                      (not (window-live-p entry-window))))
+             ;; Remove that entry.
+             (if last
+                 (setcdr last (cdr list))
+               (setq view-return-to-alist
+                     (cdr view-return-to-alist)))
+           ;; Leave entry alone.
+           (setq last entry))
+         (setq list (cdr list)))))
+    ;; Add ITEM.
+    (when item
+      (setq view-return-to-alist
+           (cons item view-return-to-alist)))))
 
 ;;;###autoload
-(defun view-mode-enter (&optional return-to exit-action) "\
-Enter View mode and set up exit from view mode depending on optional arguments.
-If RETURN-TO is non-nil it is added as an element to the buffer local alist
-view-return-to-alist.
-Save EXIT-ACTION in buffer local variable `view-exit-action'.
-It should be either nil or a function that takes a buffer as argument.
-This function will be called by `view-mode-exit'.
-
-RETURN-TO is either nil, meaning do nothing when exiting view mode, or
-it has the format (WINDOW OLD-WINDOW . OLD-BUF-INFO).
-WINDOW is a window used for viewing.
-OLD-WINDOW is nil or the window to select after viewing.
-OLD-BUF-INFO tells what to do with WINDOW when exiting.  It is one of:
-1) nil       Do nothing.
-2) t         Delete WINDOW or, if it is the only window, its frame.
+(defun view-mode-enter (&optional return-to exit-action)
+  "Enter View mode and set up exit from view mode depending on optional arguments.
+RETURN-TO non-nil means add RETURN-TO as an element to the buffer
+local alist `view-return-to-alist'.  Save EXIT-ACTION in buffer
+local variable `view-exit-action'.  It should be either nil or a
+function that takes a buffer as argument.  This function will be
+called by `view-mode-exit'.
+
+RETURN-TO is either nil, meaning do nothing when exiting view
+mode, or must have the format (WINDOW OLD-WINDOW . OLD-BUF-INFO).
+WINDOW is the window used for viewing.  OLD-WINDOW is nil or the
+window to select after viewing.  OLD-BUF-INFO tells what to do
+with WINDOW when exiting.  It is one of:
+1) nil            Do nothing.
+2) t              Delete WINDOW or, if it is the only window and
+                  `view-remove-frame-by-deleting' is non-nil, its
+                  frame.
 3) (OLD-BUFF START POINT)  Display buffer OLD-BUFF with displayed text
-                           starting at START and point at POINT in WINDOW.
+                  starting at START and point at POINT in WINDOW.
+4) quit-window    Do `quit-window' in WINDOW.
+5) keep-frame     Like case 2) but do not delete the frame.
 
-For list of all View commands, type H or h while viewing.
+For list of all View commands, type H or h while viewing.
 
 This function runs the normal hook `view-mode-hook'."
-  (if return-to
-      (let ((entry (assq (car return-to) view-return-to-alist)))
-       (if entry (setcdr entry (cdr return-to))
-         (setq view-return-to-alist (cons return-to view-return-to-alist)))))
-  (if view-mode                        ; Do nothing if already in view mode.
-      nil
+  (when return-to
+    (let ((entry (assq (car return-to) view-return-to-alist)))
+      (if entry
+         (setcdr entry (cdr return-to))
+       (setq view-return-to-alist (cons return-to view-return-to-alist)))))
+  (when exit-action
+    (setq view-exit-action exit-action))
+
+  (unless view-mode
     (view-mode-enable)
-    (if exit-action (setq view-exit-action exit-action))
     (force-mode-line-update)
-    (message "%s"
-            (substitute-command-keys "\
-Type \\[help-command] for help, \\[describe-mode] for commands, \\[View-quit] to quit."))))
-
+    (unless view-inhibit-help-message
+      (message "%s"
+              (substitute-command-keys "\
+View mode: type \\[help-command] for help, \\[describe-mode] for commands, \\[View-quit] to quit.")))))
+\f
 (defun view-mode-exit (&optional return-to-alist exit-action all-win)
-  "Exit view-mode in various ways, depending on optional arguments.
-RETURN-TO-ALIST, EXIT-ACTION and ALL-WIN determine what to do after
-exit.
-EXIT-ACTION is nil or a function that is called with current buffer as
-argument.
-RETURN-TO-ALIST is an alist that for some of the windows displaying the
-current buffer, associate information on what to do with those windows.
-If ALL-WIN or the variable `view-exits-all-viewing-windows' is non-nil,
-then all windows on RETURN-TO-ALIST are restored to their old state.
-Otherwise only the selected window is affected (if it is on RETURN-TO-ALIST).
-
-Elements of RETURN-TO-ALIST have the format (WINDOW OLD-WINDOW . OLD-BUF-INFO).
-WINDOW is a window displaying the current buffer.
-OLD-WINDOW is nil or a window to select after viewing.
-OLD-BUF-INFO is information on what to do with WINDOW and is one of:
-1) nil       Do nothing.
-2) t         Delete WINDOW or, if it is the only window, its frame.
+  "Exit View mode in various ways, depending on optional arguments.
+RETURN-TO-ALIST, EXIT-ACTION and ALL-WIN determine what to do
+after exit.  EXIT-ACTION is nil or a function that is called with
+current buffer as argument.
+
+RETURN-TO-ALIST is an alist that, for some of the windows
+displaying the current buffer, maintains information on what to
+do when exiting those windows.  If ALL-WIN is non-nil or the
+variable `view-exits-all-viewing-windows' is non-nil,
+view-mode-exit attempts to restore all windows showing the
+current buffer to their old state.  Otherwise, only the selected
+window is affected (provided it is on RETURN-TO-ALIST).
+
+Elements of RETURN-TO-ALIST must have the format
+  (WINDOW OLD-WINDOW . OLD-BUF-INFO) where
+
+WINDOW is a window displaying the current buffer and OLD-WINDOW
+is either nil or a window to select after viewing.  OLD-BUF-INFO
+provides information on what to do with WINDOW and may be one of:
+1) nil            Do nothing.
+2) t              Delete WINDOW and, if it is the only window and
+                  `view-remove-frame-by-deleting' is non-nil, its
+                  frame.
 3) (OLD-BUF START POINT)  Display buffer OLD-BUF with displayed text
-                          starting at START and point at POINT in WINDOW.
-
-If one of the WINDOW in RETURN-TO-ALIST is the selected window and the
-corresponding OLD-WINDOW is a live window, then select OLD-WINDOW."
-  (setq all-win
-       (and return-to-alist (or all-win view-exits-all-viewing-windows)))
-  (if view-mode                ; Only do something if in view mode.
-      (let* ((buffer (current-buffer))
-            window
-            (sel-old (assq (selected-window) return-to-alist))
-            (alist (cond
-                    (all-win           ; Try to restore all windows.
-                     (append return-to-alist nil)) ; Copy.
-                    (sel-old           ; Only selected window.
-                     (list sel-old))))
-            (old-window (if sel-old (car (cdr sel-old)))))
-       (if all-win                     ; Follow chains of old-windows.
-           (let ((c (length alist)) a)
-             (while (and (> c 0)       ; Safety if mutually refering windows.
-                         (or (not (window-live-p old-window))
-                             (eq buffer (window-buffer old-window)))
-                         (setq a (assq old-window alist)))
-               (setq c (1- c))
-               (setq old-window (car (cdr a))))
-             (if (or (zerop c) (not (window-live-p old-window)))
-                 (setq old-window (selected-window)))))
-       (or view-no-disable-on-exit
-           (view-mode-disable))
-       (while alist                    ; Restore windows with info.
-         (if (and (window-live-p (setq window (car (car alist))))
+                  starting at START and point at POINT in WINDOW.
+4) quit-window    Do `quit-window' in WINDOW.
+5) keep-frame     Like case 2) but do not delete the frame.
+
+If one of the WINDOW in RETURN-TO-ALIST is the selected window
+and the corresponding OLD-WINDOW is a live window, then select
+OLD-WINDOW."
+  (when view-mode                  ; Only do something if in view mode.
+    (setq all-win
+         (and return-to-alist
+              (or all-win view-exits-all-viewing-windows)))
+    (let* ((buffer (current-buffer))
+          window notlost
+          (sel-old (assq (selected-window) return-to-alist))
+          (alist (cond
+                  (all-win                ; Try to restore all windows.
+                   (append return-to-alist nil)) ; Copy.
+                  (sel-old                     ; Only selected window.
+                   (list sel-old))))
+          (old-window (if sel-old (car (cdr sel-old)))))
+      (if all-win                       ; Follow chains of old-windows.
+         (let ((c (length alist)) a)
+           (while (and (> c 0)   ; Safety if mutually refering windows.
+                       (or (not (window-live-p old-window))
+                           (eq buffer (window-buffer old-window)))
+                       (setq a (assq old-window alist)))
+             (setq c (1- c))
+             (setq old-window (car (cdr a))))
+           (if (or (zerop c) (not (window-live-p old-window)))
+               (setq old-window (selected-window)))))
+      (unless view-no-disable-on-exit
+       (view-mode-disable))
+      (while alist                         ; Restore windows with info.
+       (setq notlost nil)
+       (when (and (window-live-p (setq window (car (car alist))))
                   (eq buffer (window-buffer window)))
-             (let ((frame (window-frame window))
-                   (old-buf-info (cdr (cdr (car alist)))))
-               (if all-win (select-window window))
-               (cond
-                ((and (consp old-buf-info) ; Case 3.
-                      (buffer-live-p (car old-buf-info)))
-                 (set-window-buffer window (car old-buf-info)) ; old-buf
-                 (set-window-start window (car (cdr old-buf-info)))
-                 (set-window-point window (car (cdr (cdr old-buf-info)))))
-                ((not (eq old-buf-info t)) nil) ; Not case 2, do nothing.
-                ((not (one-window-p t)) (delete-window))
-                ((not (eq frame (next-frame)))
-                 ;; Not the only frame, so can safely be removed.
-                 (if view-remove-frame-by-deleting
-                     (delete-frame frame)
-                   (iconify-frame frame))))))
-         (setq view-return-to-alist (delete (car alist) view-return-to-alist))
-         (setq alist (cdr alist)))
-       (if (window-live-p old-window)  ; still existing window
-           (select-window old-window))
-;      (if (and exit-action (not (get-buffer-window buffer)))
-       (if exit-action
-           (progn (setq view-exit-action nil)
-                  (funcall exit-action buffer)))
-       (force-mode-line-update))))
-
+         (let ((frame (window-frame window))
+               (old-buf-info (cdr (cdr (car alist)))))
+           (if all-win (select-window window))
+           (cond
+            ((and (consp old-buf-info)         ; Case 3.
+                  (buffer-live-p (car old-buf-info)))
+             (set-window-buffer window (car old-buf-info)) ; old-buf
+             (set-window-start window (car (cdr old-buf-info)))
+             (set-window-point window (car (cdr (cdr old-buf-info)))))
+            ((eq old-buf-info 'quit-window)
+             (quit-window))                    ; Case 4.
+            (old-buf-info                      ; Case 2 or 5.
+             (cond
+              ((not (one-window-p t))          ; Not only window.
+               (delete-window))
+              ((eq old-buf-info 'keep-frame)   ; Case 5.
+               (bury-buffer))
+              ((not (eq frame (next-frame)))  ; Case 2 and only window.
+               ;; Not the only frame, so can safely be removed.
+               (if view-remove-frame-by-deleting
+                   (delete-frame frame)
+                 (setq notlost t)         ; Keep the window. See below.
+                 (iconify-frame frame))))))))
+       ;; If a frame is removed by iconifying it, the window is not
+       ;; really lost.  In this case we keep the entry in
+       ;; `view-return-to-alist' so that if the user deiconifies the
+       ;; frame and then hits q, the frame is iconified again.
+       (unless notlost
+         (with-current-buffer buffer
+           (setq view-return-to-alist
+                 (delete (car alist) view-return-to-alist))))
+       (setq alist (cdr alist)))
+      (when (window-live-p old-window)
+       ;; old-window is still alive => select it.
+       (select-window old-window))
+      (when exit-action
+       ;; Don't do that: If the user wants to quit the *Help* buffer a
+       ;; second time it won't have any effect.
+;;;    (setq view-exit-action nil)
+       (funcall exit-action buffer))
+      (force-mode-line-update))))
+\f
 (defun View-exit ()
   "Exit View mode but stay in current buffer."
   (interactive)
@@ -553,7 +685,8 @@ corresponding OLD-WINDOW is a live window, then select OLD-WINDOW."
 (defun View-exit-and-edit ()
   "Exit View mode and make the current buffer editable."
   (interactive)
-  (let ((view-old-buffer-read-only nil))
+  (let ((view-old-buffer-read-only nil)
+       (view-no-disable-on-exit nil))
     (view-mode-exit)))
 
 (defun View-leave ()
@@ -579,7 +712,7 @@ previous state and go to previous buffer or window."
   "Quit View mode, kill current buffer and return to previous buffer."
   (interactive)
   (view-mode-exit view-return-to-alist (or view-exit-action 'kill-buffer) t))
-
+\f
 
 ;;; Some help routines.
 
@@ -613,7 +746,8 @@ previous state and go to previous buffer or window."
 
 (defun view-set-half-page-size-default (lines)
   ;; Get and maybe set half page size.
-  (if (not lines) view-half-page-size
+  (if (not lines) (or view-half-page-size
+                     (/ (view-window-size) 2))
     (setq view-half-page-size
          (if (zerop (setq lines (prefix-numeric-value lines)))
              (/ (view-window-size) 2)
@@ -646,7 +780,7 @@ Also set the mark at the position where point was."
 ;    (goto-char (point-max))
 ;    (beginning-of-line))
 ;  (view-recenter))
-  
+
 (defun View-goto-line (&optional line)
   "Move to first (or prefix LINE) line in View mode.
 Display is centered at LINE.
@@ -656,14 +790,16 @@ Also set the mark at the position where point was."
   (goto-line line)
   (view-recenter))
 
-(defun View-scroll-to-buffer-end ()
-  "Scroll backward or forward so that buffer end is at last line of window."
+(defun View-back-to-mark (&optional ignore)
+  "Return to last mark set in View mode, else beginning of file.
+Display that line at the center of the window.
+This command pops the mark ring, so that successive
+invocations return to earlier marks."
   (interactive)
-  (let ((p (if (pos-visible-in-window-p (point-max)) (point))))
-    (goto-char (point-max))
-    (recenter -1)
-    (and p (goto-char p))))
-
+  (goto-char (or (mark t) (point-min)))
+  (pop-mark)
+  (view-recenter))
+\f
 (defun view-scroll-lines (lines backward default maxdefault)
   ;; This function does the job for all the scrolling commands.
   ;; Scroll forward LINES lines.  If BACKWARD is true scroll backwards.
@@ -672,8 +808,8 @@ Also set the mark at the position where point was."
   ;; window full.
   (if (or (null lines) (zerop (setq lines (prefix-numeric-value lines))))
       (setq lines default))
-  (if (< lines 0)
-      (progn (setq backward (not backward)) (setq lines (- lines))))
+  (when (< lines 0)
+    (setq backward (not backward)) (setq lines (- lines)))
   (setq default (view-page-size-default nil)) ; Max scrolled at a time.
   (if maxdefault (setq lines (min lines default)))
   (cond
@@ -698,19 +834,20 @@ Also set the mark at the position where point was."
             (bufname (buffer-name))
             (file (buffer-file-name)))
         (or (not view-try-extend-at-buffer-end)
-            (not file)
+            (null file)
             (verify-visited-file-modtime buf)
             (not (file-exists-p file))
-            (and (buffer-modified-p buf)
-                 (setq file (file-name-nondirectory file))
-                 (not (yes-or-no-p
-                       (format
-                        "File %s changed on disk.  Discard your edits%s? "
-                        file
-                        (if (string= bufname file) ""
-                          (concat " in " bufname))))))
-            (progn (revert-buffer t t t)
-                   (pos-visible-in-window-p (point-max)))))))
+            (when (buffer-modified-p buf)
+              (setq file (file-name-nondirectory file))
+              (not (yes-or-no-p
+                    (format
+                     "File %s changed on disk.  Discard your edits%s? "
+                     file
+                     (if (string= bufname file) ""
+                       (concat " in " bufname))))))
+            (progn
+              (revert-buffer t t t)
+              (pos-visible-in-window-p (point-max)))))))
 
 (defun view-end-message ()
   ;; Tell that we are at end of buffer.
@@ -721,6 +858,14 @@ Also set the mark at the position where point was."
                (if view-scroll-auto-exit "\\[View-scroll-page-forward]"
                  "\\[View-quit]")))
     (message "End of buffer")))
+\f
+(defun View-scroll-to-buffer-end ()
+  "Scroll backward or forward so that buffer end is at last line of window."
+  (interactive)
+  (let ((p (if (pos-visible-in-window-p (point-max)) (point))))
+    (goto-char (point-max))
+    (recenter -1)
+    (and p (goto-char p))))
 
 (defun View-scroll-page-forward (&optional lines)
   "Scroll \"page size\" or prefix LINES lines forward in View mode.
@@ -730,14 +875,14 @@ Exit if end of text is visible and `view-scroll-auto-exit' is non-nil.
 \\[View-scroll-page-backward-set-page-size].
 If LINES is more than a window-full, only the last window-full is shown."
   (interactive "P")
-  (view-scroll-lines lines nil view-page-size nil))
+  (view-scroll-lines lines nil (view-page-size-default view-page-size) nil))
 
-(defun View-scroll-page-backward (&optional lines) 
+(defun View-scroll-page-backward (&optional lines)
   "Scroll \"page size\" or prefix LINES lines backward in View mode.
 See also `View-scroll-page-forward'."
   (interactive "P")
-  (view-scroll-lines lines t view-page-size nil))
-  
+  (view-scroll-lines lines t (view-page-size-default view-page-size) nil))
+
 (defun View-scroll-page-forward-set-page-size (&optional lines)
   "Scroll forward LINES lines in View mode, setting the \"page size\".
 This is the number of lines which \\[View-scroll-page-forward] and
@@ -798,20 +943,10 @@ If you don't specify a prefix argument, it uses the number of lines set by
 \\[View-scroll-page-backward-set-page-size].
 If LINES is more than a window-full, only the last window-full is shown."
   (interactive "P")
-  (let ((view-mode-auto-exit nil)
+  (let ((view-scroll-auto-exit nil)
        (view-try-extend-at-buffer-end t))
-    (view-scroll-lines lines nil view-page-size nil)))
-
-(defun View-back-to-mark (&optional ignore)
-  "Return to last mark set in View mode, else beginning of file.
-Display that line at the center of the window.
-This command pops the mark ring, so that successive
-invocations return to earlier marks."
-  (interactive)
-  (goto-char (or (mark t) (point-min)))
-  (pop-mark)
-  (view-recenter))
-            
+    (view-scroll-lines lines nil (view-page-size-default view-page-size) nil)))
+\f
 (defun View-search-regexp-forward (n regexp)
   "Search forward for first (or prefix Nth) occurrence of REGEXP in View mode.
 
@@ -828,7 +963,7 @@ for use by later search commands.
 
 The variable `view-highlight-face' controls the face that is used
 for highlighting the match that is found."
-  (interactive "p\nsSearch forward (regexp): ") 
+  (interactive "p\nsSearch forward (regexp): ")
   (view-search n regexp))
 
 (defun View-search-regexp-backward (n regexp)
@@ -872,6 +1007,12 @@ for highlighting the match that is found."
 
 (defun view-search (times regexp)
   ;; This function does the job for all the View-search- commands.
+  ;; Search for the TIMESt match for REGEXP. If TIMES is negative
+  ;; search backwards. If REGEXP is nil use `view-last-regexp'.
+  ;; Charcters "!" and "@" have a special meaning at the beginning of
+  ;; REGEXP and are removed from REGEXP before the search "!" means
+  ;; search for lines with no match for REGEXP.  "@" means search in
+  ;; the whole buffer, don't start searching from the present point.
   (let (where no end ln)
     (cond
      ((and regexp (> (length regexp) 0)
@@ -885,7 +1026,7 @@ for highlighting the match that is found."
       (setq view-last-regexp (if no (list regexp) regexp)))
      ((consp view-last-regexp)
       (setq regexp (car view-last-regexp))
-      (if (not (setq no (not no))) (setq view-last-regexp regexp)))
+      (unless (setq no (not no)) (setq view-last-regexp regexp)))
      (view-last-regexp (setq regexp view-last-regexp)
                       (if no (setq view-last-regexp (list regexp))))
      (t (error "No previous View-mode search")))
@@ -910,28 +1051,29 @@ for highlighting the match that is found."
               times (if no "no " "") regexp)
       (sit-for 4))))
 
+;; This is the dumb approach, looking at each line.  The original
+;; version of this function looked like it might have been trying to
+;; do something clever, but not succeeding:
+;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2007-09/msg00073.html
 (defun view-search-no-match-lines (times regexp)
-  ;; Search for the TIMESt occurrence of line with no match for REGEXP.
-  (let ((back (and (< times 0) (setq times (- times)) -1))
-       n)
-    (while (> times 0)
-      (save-excursion (beginning-of-line (if back (- times) (1+ times)))
-                     (setq n (point)))
-      (setq times
-           (cond
-            ((< (count-lines (point) n) times) -1) ; Not enough lines.
-            ((or (null (re-search-forward regexp nil t back))
-                 (if back (and (< (match-end 0) n)
-                               (> (count-lines (match-end 0) n) 1))
-                   (and (< n (match-beginning 0))
-                        (> (count-lines n (match-beginning 0)) 1))))
-             0)                        ; No match within lines.
-            (back (count-lines (max n (match-beginning 0)) (match-end 0)))
-            (t (count-lines (match-beginning 0) (min n (match-end 0))))))
-      (goto-char n))
-    (and (zerop times) (looking-at "^.*$"))))
-
+  "Search for the TIMESth occurrence of a line with no match for REGEXP.
+If such a line is found, return non-nil and set the match-data to that line.
+If TIMES is negative, search backwards."
+  (let ((step (if (>= times 0) 1
+                (setq times (- times))
+                -1)))
+    ;; Note that we do not check the current line.
+    (while (and (> times 0)
+                (zerop (forward-line step)))
+      ;; (forward-line 1) returns 0 on moving within the last line.
+      (if (eobp)
+          (setq times -1)
+        (or (re-search-forward regexp (line-end-position) t)
+            (setq times (1- times))))))
+  (and (zerop times)
+       (looking-at ".*")))
 
 (provide 'view)
 
+;;; arch-tag: 6d0ace36-1d12-4de3-8de3-1fa3231636d7
 ;;; view.el ends here