;;; jde-help.el
;; $Revision: 1.2 $ 

;; Author: Paul Kinnucan <paulk@mathworks.com>, Phillip Lord <plord@hgmp.mrc.ac.uk>
;; Maintainer: Paul Kinnucan
;; Keywords: java, tools

;; Copyright (C) 1999, 2001 Paul Kinnucan.

;; 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)
;; any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; 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.

(require 'beanshell)
(require 'jde-widgets)
(require 'eieio)

(defcustom jde-help-docsets nil
  "*Lists collections of HTML files documenting Java classes. 
This list is used by the `jde-help-class' command to find help for 
a class. You can specify the following information for each docset:

Docset type

  The following types are valid: 

  * javadoc. 

    Collections generated by the javadoc command.

  * Other

    Collections of HTML class documentation files generated by some
    other means.

Docset directory

   Directory containing the collection, e.g., d:/jdk1.2/docs/api.

Doc lookup function

   Should specify a function that accepts a fully qualified class name, 
   e.g., java.awt.String, and a docset directory and returns a path to 
   an HTML file that documents that class, e.g., 
   d:/jdk1.2/docs/api/java/awt/String.html. This field must be specified
   for non-javadoc collections. It is ignored for javadoc colletions.
"
  :group 'jde-project
  :type '(repeat 
	  (list
	   (radio-button-choice
	    :format "%t \n%v"
	    :tag "Docset type:"
	    (const "javadoc")
	    (const "Other"))
	   (file :tag "Docset directory")
	   (function :tag "Doc lookup function:"))))

(defun jde-help-docset-get-type (docset)
  (nth 0 docset))

(defun jde-help-docset-get-dir (docset)
  (nth 1 docset))

(defun jde-help-docset-get-lookup-function (docset)
  (nth 2 docset))


(defun jde-help-lookup-java1-javadoc (class docset-dir) 
  (let ((doc-path
	 (concat 
	  (expand-file-name class docset-dir)
	  ".html")))
    (if (file-exists-p doc-path) 
	doc-path)))

(defun jde-help-lookup-java2-javadoc (class docset-dir) 
  (let ((doc-path
	 (concat 
	  (expand-file-name 
	   (substitute ?/ ?. class) 
	   docset-dir)
	  ".html")))
    (if (file-exists-p doc-path) 
	doc-path)))


(defun jde-help-get-doc (class) 
"Gets path to the HTML file for CLASS where CLASS is a 
qualified class name."
  (if class
      (cond 
       ((not jde-help-docsets)
	(error "%s" "Help error: No docsets available. See jde-help-docsets."))
       (t
	(let ((paths
	       (mapcar
		(lambda (docset)
		  (cond
		   ((string= (jde-help-docset-get-type docset) "javadoc")
		    (or 
		     (jde-help-lookup-java1-javadoc
		      class
		      (jde-help-docset-get-dir docset)) 
		     (jde-help-lookup-java2-javadoc
		      class
		      (jde-help-docset-get-dir docset))))
		   (t
		    (apply
		     (jde-help-docset-get-lookup-function docset)
		     class
		     (jde-help-docset-get-dir docset)))))
		jde-help-docsets)))
	  (setq paths (delq nil paths))
	  ;; Return first file found.
	  (if paths (car paths) paths))))))


(defun jde-help-symbol ()
  "Displays help for the symbol at point. The symbol may reference an object, a class,
or a method or field. If the symbol references a class, this function displays the 
javadoc for the class. If the symbol references an object,  this method 
displays the javadoc for the class of the object. If the symbol references a field or
a method, this function displays the javadoc for the class of the object of which
the field or method is a member. Eventually this function will be enhanced to position
the javadoc at the point where the method or field is documented."
  (interactive)
  (condition-case err
      (let* ((unqualified-name (thing-at-point 'symbol))
	     (class-names 
	      (bsh-eval-r 
	       (concat "jde.util.JdeUtilities.getQualifiedName(\"" unqualified-name
		       "\");"))))
	(if (not class-names)
	    (let ((parse-result (jde-help-parse-symbol-at-point)))
	      (if parse-result	      
		  (setq unqualified-name  (car parse-result)))
	      (setq class-names 
		    ;;expand the names into full names, or a list of names
		    (bsh-eval-r 
		     (concat "jde.util.JdeUtilities.getQualifiedName(\"" unqualified-name "\");")))))
   
	;;Check return value of QualifiedName
	(if class-names
	    (let ((doc-files (mapcar 'jde-help-get-doc class-names)))
	      (if doc-files
		  (progn
		    ;;Remove any annoying nils from the returned values
		    (setq doc-files (delq nil doc-files))
		    (if (eq 1 (length doc-files))
			;;then show it
			(jde-help-show-document (car doc-files))
		      ;;else let the user choose
		      ;;If the list is only one long
		      (jde-help-choose-document doc-files)))
		(error "Cannot find documentation for %s" unqualified-name)))
	  (error "Cannot find %s" unqualified-name)))
    (error
     (message "%s" (error-message-string err)))))
  

(defun jde-help-show-document (doc-file)
  "Actually displays the document."
  (if (not (eq doc-file nil))
      (browse-url (format "file://%s" doc-file))))

(defun jde-help-choose-document(doc-files)
  "Allows the user to select which of the possible documentation files they wish to view."
  (let ((buf (get-buffer-create "*Select Class*" )))
    ;; (setq jde-help-documentation-files doc-files)
    (setq jde-help-selected-documentation-file (car doc-files))
    (set-buffer buf)
    (widget-insert "Several documentation files match your class.\n")
    (widget-insert "Select the one you want to view.\n")
    (widget-insert "Then click the OK button.\n\n" )
    (let ((args (list
		 'radio-button-choice
		 :value (car doc-files)
		 :notify (lambda (widget &rest ignore)
			   (setq jde-help-selected-documentation-file (widget-value widget))
			   (message "You selected: %s"
				    (widget-value widget))))))
	  (setq args (nconc
		      args
		       (mapcar (lambda (x) (list 'item x)) doc-files)))
	  (apply 'widget-create args))
    (widget-insert "\n")
    (widget-create 'push-button
		   :notify (lambda (&rest ignore)
			     (let ((dialog-buffer
				    (current-buffer)))
			       (delete-window)
			       (kill-buffer dialog-buffer)
			       (jde-help-show-document jde-help-selected-documentation-file)
			       (message "Viewing initiated.")))
		   "Ok")
    (use-local-map widget-keymap)
    (widget-setup)
    (pop-to-buffer buf)))


(defun jde-help-parse-symbol-at-point ()
  "Returns (cons TYPE MEMBER) where TYPE is the declared type of
the object referenced by the (qualified) name at point and MEMBER is the
field or method referenced by the name if qualified."
  (let ((parse-result (jde-parse-qualified-name-at-point)))
    (if parse-result
	(let* ((qualifier (car parse-result))
	       (name (cdr parse-result))
	       (obj (if qualifier qualifier name))
	       (member (if qualifier name)))
	  (if (not
	       (and 
		qualifier
		(string-match "[.]" qualifier)))
	      (let ((declared-type (jde-parse-declared-type-of obj)))
		(if declared-type
		    (cons declared-type  member))))))))



;;Support for auto open of source code. This is mostly a hack from 
;;jde-help.el. Probably the two should be changed to use the
;;same methods over again...
(defun jde-show-class-source ( &optional unqual-class )
  "Displays source of the class whose name appears at point in the current
Java buffer. This command finds only classes that reside in the source paths
specified by `jde-db-source-directories'. You should provide a global setting
for this variable in your .emacs file to accommodate source files that are
not associated with any project."
  (interactive)
  (condition-case err
      (let* ((unqualified-name 
 	      (or unqual-class
		  (read-from-minibuffer "Class: " (thing-at-point 'symbol))))
 	     (class-names 
 	      ;;expand the names into full names, or a list of names
 	      (bsh-eval-r 
 	       (concat 
 		"jde.util.JdeUtilities.getQualifiedName(\"" 
 		unqualified-name "\");"))))
 	;;Check return value of QualifiedName
 	(if (eq nil class-names)
 	    (error "Cannot find %s" unqualified-name))
	;; Turn off switching project settings to avoid 
	;; resetting jde-db-source-directories.
	(let ((old-value jde-project-context-switching-enabled-p))
	  (setq jde-project-context-switching-enabled-p nil)
	  ;;If the list is only one long
	  (if (eq 1 (length class-names))
	      ;;then show it
	      (progn(other-window 1)
		    (jde-find-class-source (car class-names)))
	     	  ;;else let the user choose
	    (let ((dialog
		   (jde-show-class-source-chooser-dialog
		    "show sources dialog"
		    :classes class-names)))
	      (jde-dialog-show dialog)))
	  (setq jde-project-context-switching-enabled-p old-value)))
    (error
     (message "%s" (error-message-string err)))))


(defclass jde-show-class-source-chooser-dialog (jde-dialog)
  ((classes     :initarg :classes
		:type list
		:documentation
		"Classes that match the unqualified class name.")
   (check-boxes :initarg :check-boxes
		:documentation
		"Radio buttons used to select source file."))
  "Dialog used to specify which classes to show the source for.")

(defmethod jde-dialog-create ((this jde-show-class-source-chooser-dialog))
    (widget-insert "Several classes match the name you specified.\n")
    (widget-insert "Select the ones you want to view.\n")
    (widget-insert "Then click the OK button.\n\n" )

    (let ((items
	   (mapcar
	    (lambda (class)
	      (list
	       'const
	       :format "%v"
	       class))
	    (oref this classes))))

      (oset this check-boxes
	    (widget-create
	     (list 'checklist :entry-format " %b %v\n" :args items)))
      (widget-insert "\n")))

(defmethod jde-dialog-ok ((this jde-show-class-source-chooser-dialog))
  (let ((dialog-buffer (current-buffer)))
    (mapc (lambda (x) 
	    (other-window 1)
	    (jde-find-class-source x)) 
	  (widget-value (oref this check-boxes)))
    (kill-buffer dialog-buffer)))



(provide 'jde-help)

;; $Log: jde-help.el,v $
;; Revision 1.2  2001/02/12 05:38:25  paulk
;; JDE 2.2.7
;;
;; Revision 1.15  2001/02/04 01:31:13  paulk
;; Changed declaration of customized variables to permit completion of paths.
;;
;; Revision 1.14  2000/10/08 12:55:39  paulk
;; *** empty log message ***
;;
;; Revision 1.13  2000/08/12 04:47:10  paulk
;; Fixed regression error in jde-help-symbol-at-point.
;;
;; Revision 1.12  2000/02/09 05:06:49  paulk
;; Replaced jde-help-class with jde-help-symbol method. The new method
;; gets help for the symbol at point. The symbol may refer to a class,
;; an object, or a method or field.
;;
;; Revision 1.11  2000/02/01 04:11:56  paulk
;; ReleaseNotes.txt
;;
;; Revision 1.10  2000/01/18 07:11:25  paulk
;; Added jde-show-class-source. Thanks to Phil Lord for the initial
;; implementation of this command.
;;
;; Revision 1.9  2000/01/15 08:06:25  paulk
;; Eliminated some globally bound symbols.
;;
;; Revision 1.8  1999/09/30 04:46:10  paulk
;; Fixed typo spotted by David Biesack.
;;
;; Revision 1.7  1999/09/18 03:26:39  paulk
;; Now prepends "file://" to doc file when invoking browse-url. Hopefully
;; this will fix the problem reported by one user where the browser
;; prepends http://www to doc file path.
;;
;; Revision 1.6  1999/08/20 00:44:43  paulk
;; Corrected spelling of Phillip Lord's name.
;;
;; Revision 1.5  1999/06/26 00:00:12  paulk
;; Type javadoc now sufficient to specify both Java 1 and Java 2 javadoc docsets.
;;
;; Revision 1.4  1999/06/25 01:38:17  paulk
;; Enhanced to support doc collections of any type.
;;
;; Revision 1.3  1999/06/17 22:27:33  paulk
;; Bug fix.
;;
;; Revision 1.2  1999/06/17 21:53:05  paulk
;; Eliminated separate customization group for help variables.
;;
;; Revision 1.1  1999/06/17 21:47:15  paulk
;; Initial revision
;;

;; End of jde-help.el
