;;!emacs
;; $Id:
;;
;; FILE:         hmous-info.el
;; SUMMARY:      Walks through Info networks using one key.
;; USAGE:        GNU Emacs Lisp Library, functions called by smart-key.el
;;
;; AUTHOR:       Bob Weiner
;; ORIG-DATE:    04-Apr-89
;; LAST-MOD:      3-Feb-93 at 15:32:26 by Bob Weiner
;;
;; This file is for use with Hyperbole.
;;
;; Copyright (C) 1989, 1990, 1991  Brown University and the Free Software
;; Foundation, Inc.
;; Available for use and distribution under the same terms as GNU Emacs.
;;
;;
;; DESCRIPTION:  
;;
;;  This code is machine independent.
;;
;;  To install:  See hui-mouse.el
;;
;; DESCRIP-END.

(defun smart-info ()
  "Walks through Info documentation networks using one key or mouse key.

If key is pressed within:
 (1) the first line of an Info Menu Entry or Cross Reference, the desired node
       is found;
 (2) the Up,Next,or Previous entries of a Node Header (first line),
       the desired node is found;
 (3) the File entry of a Node Header (first line),       
       the 'Top' node within that file is found;
 (4) at the end of the current node, the Next node is found (this will
     descend subtrees if the function 'Info-global-next' is bound);
 (5) anywhere else (e.g. at the end of a line), the current node entry is
       scrolled up one screen

Returns t if key is pressed within an Info Node Header, Cross Reference,
or a Menu; otherwise returns nil."

  (interactive)
  (cond 
    ;;
    ;; If at end of node, go to next node
    ;;
    ((last-line-p)
     (if (fboundp 'Info-global-next) (Info-global-next)
       (Info-next)))
    ((Info-handle-in-node-hdr))
    ((Info-handle-in-note))
    ((Info-handle-in-menu))
    ((pos-visible-in-window-p (point-max))
     (if (fboundp 'Info-global-next) (Info-global-next)
       (Info-next)))
    ;;
    ;; If nothing else scroll forward a screen.
    ;;
    ((scroll-up-eol))))

(defun smart-info-meta ()
  "Walks through Info documentation networks using one meta-key or mouse meta-key.

If meta-key is pressed within:
 (1) the first line of an Info Menu Entry or Cross Reference, the desired node
       is found;
 (2) the Up,Next,or Previous entries of a Node Header (first line),
       the last node in the history list is found;
 (3) the File entry of a Node Header (first line),       
       the 'DIR' root-level node is found;
 (4) at the end of the current node, the Previous node is found (this will
       return from subtrees if the function 'Info-global-prev is bound);
 (5) anywhere else (e.g. at the end of a line), the current node entry is
       scrolled down one screen

Returns t if meta-key is pressed within an Info Node Header, Cross Reference,
or a Menu; otherwise returns nil."

  (interactive)
  (cond
    ;;
    ;; If at end or beginning of node, go to previous node
    ;;
    ((last-line-p)
     (if (fboundp 'Info-global-prev) (Info-global-prev)
       (Info-prev)))
    ((Info-handle-in-node-hdr-meta))
    ((Info-handle-in-note))
    ((Info-handle-in-menu))
    ((pos-visible-in-window-p (point-min))
     (if (fboundp 'Info-global-prev) (Info-global-prev)
       (Info-prev)))
    ;;
    ;; If anywhere else, scroll backward a screen.
    ;;
    ((scroll-down-eol))))

(defun Info-handle-in-node-hdr ()
  "If within an Info node header, move to <FILE>Top, <Up>, <Previous>, or
<Next> node, depending on which label point is on, and return t.
Otherwise, return nil."
  ;;
  ;; Test if on 1st line of node, i.e. node header
  ;;
  (if (not (first-line-p))
      nil
    (let ((nodename "Top") (filep nil))
      (save-excursion
	(if (and
	      (re-search-forward "[:, \t\n]" nil t)
	      (re-search-backward
		"\\(File\\|Node\\|Up\\|Prev\\|Previous\\|Next\\):[ \t]" nil t))
	    (progn (setq filep (string-equal
				 "file"
				 (downcase (buffer-substring
					     (match-beginning 1)
					     (match-end 1)))))
		   (if (re-search-forward (concat ":[ \n]\\([^,\t\n"
						  (if filep " ")
						  "]*\\)") nil t)
		       (setq nodename (buffer-substring
					(match-beginning 1)
					(match-end 1)))))
	  (error "Node header not found.")))
      (setq nodename
	    (cond ((= (aref nodename 0) ?\() nodename)
		  (filep (concat "(" nodename ")" "Top"))
		  (buffer-file-name (concat "(" buffer-file-name ")" nodename))
		  (t nodename)))
      (Info-goto-node nodename)
      t)))

(defun Info-handle-in-node-hdr-meta ()
  "If within an Info node header when the 'smart-info-meta' command is
executed, when within the <FILE> header go to the DIR top-level node.  When
within any other header (<Up>, <Previous>, or <Next>) go to last node from
history list.  Return t if in Info node header.  Otherwise return nil."
  ;;
  ;; Test if on 1st line of node, i.e. node header
  ;;
  (if (not (first-line-p))
      nil
    (save-excursion
      (if (and 
	    (re-search-forward "[:, \t\n]" nil t)
	    (re-search-backward
	      "\\(File\\|Node\\|Up\\|Prev\\|Previous\\|Next\\):[ \t]" nil t) )
	  ;; If in <FILE> hdr
	  (progn (if (string-equal
		       "file"
		       (downcase (buffer-substring
				   (match-beginning 1)
				   (match-end 1))))
		     (Info-directory)
		   (Info-last))
		 t)
	(error "Node header not found.")
	nil))))

(defun Info-handle-in-note ()
  "Follows an Info cross-reference.
If point is within the first line of an Info note (cross-reference), follows
cross-reference and returns t; otherwise returns nil."
  (let ((note-name) (bol) (opoint (point)))
    (save-excursion
      (beginning-of-line)
      (setq bol (point))
      (end-of-line)
      (if (and (re-search-backward "\*\\(Note\\|Ref\\)\\([ \t]+\\|$\\)" bol t)
	       (<= (point) opoint)
	       (looking-at "\*\\(Note\\|Ref\\)[ \t\n]+\\([^:]*\\):"))
	  ;; Remove newline and extra spaces from 'note-name'
	  (setq note-name (hypb:replace-match-string
			   "[ \n\t]+"
			   (buffer-substring
			    (match-beginning 2) (match-end 2))
			   " "))))
    (if note-name
	(progn (Info-follow-reference note-name) t))))

(defun Info-handle-in-menu ()
  "Displays node referred to by an Info Menu Entry.
If point is within an Info menu entry, goes to node referenced by
entry and returns t; otherwise returns nil."
  ;;
  ;; Test if there is a menu in this node
  ;;
  (let ((in-menu nil) (curr-point (point)))
    (save-excursion
      (goto-char (point-min))
      (setq in-menu 
	    (and (search-forward "\n* menu:" nil t)
		 (< (point) curr-point))))
    (if (not in-menu)
	nil
      (forward-char) ; Pass '*' char if point is in front of
      (if (search-backward "\n*" nil t)
	  (progn (forward-char 2)
		 (Info-goto-node (Info-extract-menu-node-name))))
      t)))

(provide 'hmous-info)
