;;; -*- Mode: LISP; Syntax: Common-Lisp; Package: GARNET-GADGETS; Base: 10 -*-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;         The Garnet User Interface Development Environment.      ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This code was written as part of the Garnet project at          ;;;
;;; Carnegie Mellon University, and has been placed in the public   ;;;
;;; domain.  If you are using this code or any part of Garnet,      ;;;
;;; please contact garnet@cs.cmu.edu to be put on the mailing list. ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;;  Error Gadget and Query Gadget Utility file
;;;   This file contains the functions used by both motif- and regular
;;;   error gadgets
#|
============================================================
Change log:
 09/22/93  Bruno Haible - Added CLISP version of Careful-Eval
 01/25/93  Andrew Mickish - Call to notice-items-changed --> opal:update in
             internal-display-q-or-e
 12/12/92  Andrew Mickish - Added Careful-Eval-Formula-Lambda
 12/10/92  Andrew Mickish - *drawable-to-window-mapping* ---> *garnet-windows*
 10/20/92  Andrew Mickish - Added Careful-Eval, Careful-String-Eval,
             Careful-Read-From-String
 08/21/91  Brad Myers - fixed race condition in wait-interaction-complete
 08/21/91  Andrew Mickish - Made string parameter to display-* optional
 08/4/92   Brad Myers - inter:beep is optional, and make window visible before
                        calling sub-menu
 06/5/92   Brad Myers - used :modal-p windows instead of error-priority-level
 05/19/92  Brad Myers - Raise window in internal-display-q-or-e
 04/23/92  Ed Pervin - Added schema-p checks to e-q-gadget-sel-func
 04/8/92   Brad Myers - created from error-gadget
============================================================
|#

(in-package "GARNET-GADGETS" :use '("LISP" "KR"))

(export '(Display-Error Display-Error-And-Wait
	  Display-Query Display-Query-And-Wait
	  Careful-Eval Careful-String-Eval Careful-Read-From-String
	  Careful-Eval-Formula-Lambda))

(defun internal-display-q-or-e (gadget string wait labels)
  ;; Set the message to be displayed
  (s-value gadget :string string)
  ;; Set the label list
  (when (and labels
	     (not (equal labels (g-value gadget :button-names))))
    (s-value gadget :button-names labels)
    ;; Invoke the :fix-update-slots method of the aggrelist to adjust
    ;; the button-panel's components according to the new items
    (opal:update (g-value gadget :window))
    )
  ;; Set up window
  (let ((window (g-value gadget :window)))
    ;; Set the position and dimensions of the window
    (s-value window :left (g-value gadget :window-left))
    (s-value window :top (g-value gadget :window-top))
    (s-value window :width (g-value gadget :window-width))
    (s-value window :height (g-value gadget :window-height))
    (s-value gadget :waiting wait)
    (s-value window :visible T)
    ;; beep if desired
    (when (g-value gadget :beep-p) (inter:beep))
    ;; wait if desired, updating window
    (if wait
	(inter:wait-interaction-complete window) ;; will raise the window
	;; else raise window here
	(opal:raise-window window))))   ;; calls update


(defun DISPLAY-ERROR (error-gadget &optional
		      (string (g-value error-gadget :string)))
  (internal-display-q-or-e error-gadget string NIL NIL))

(defun DISPLAY-ERROR-AND-WAIT (error-gadget &optional
			       (string (g-value error-gadget :string)))
  (internal-display-q-or-e error-gadget string T NIL))

(defun DISPLAY-QUERY (query-gadget &optional
		      (string (g-value query-gadget :string))
		      label-list)
  (internal-display-q-or-e query-gadget string NIL label-list))

(defun DISPLAY-QUERY-AND-WAIT (query-gadget &optional
			       (string (g-value query-gadget :string))
			       label-list)
  (internal-display-q-or-e query-gadget string T label-list))

(defun E-Q-Gadget-Sel-Func (button value)
  (let* ((window (g-value button :window))
	 (error-gadget (g-value button :parent))
	 (waiting (g-value error-gadget :waiting)))
    ;; make this query window go away first, in case selection
    ;; function brings up another window.
    (when (schema-p window)
      (s-value window :visible NIL)
      (opal:update window))
    (kr-send error-gadget :selection-function
	     error-gadget value)
    ;; use stored value in case selection-function destroys the
    ;; error-gadget, still need to get out of the waiting loop.
    (when waiting
      (inter:interaction-complete value))))

(defun Error-Gadget-Destroy (error-gadget &optional erase)
  ;; first, remove the gadget from its window so when the window is
  ;; destroyed, the gadget will not be.  Then destroy the gadget itself
  ;; using call-prototype-method
  (let ((agg (g-value error-gadget :parent))
	(window (g-value error-gadget :window)))
    (if agg
	(opal:remove-component agg error-gadget))
    ;; make sure window isn't already being destroyed
    (when (and window
	       (schema-p window)
	       (getf (xlib:drawable-plist (get-local-value window :drawable))
		     :garnet))
      (opal:destroy window)))
  (call-prototype-method error-gadget erase))


;; This function takes an expression to evaluate.  If there is an error
;; detetected during the eval, then the error gadget is raised with the
;; error message.  The function returns two values: the result of the eval 
;; and a flag indicating whether there was an error.
;;
;;  Example:  (gg:Careful-Eval `(read-from-string ,string))

(defmacro Careful-Eval (form &optional an-error-gadget error-message)
  #-clisp
  `(#+allegro-V3.1 excl::handler-case
    #+lucid lcl::handler-case
    #-(or allegro-v3.1 lucid) handler-case
    (eval ,form)
    (error (condition)
     (when ,an-error-gadget
       (let ((str (if ,error-message
		      (format NIL "~A~%~%~A" condition ,error-message)
		      (format NIL "~A" condition))))
	 (display-error ,an-error-gadget str)))
     (values NIL condition)))
  #+clisp
  `(let ((*error-handler*
	  #'(lambda (continue errorstring &rest args)
	      (if continue
                  (let ((*error-handler* nil))
                    (apply #'cerror continue errorstring args))
                  (let ((condition errorstring))
                    (when ,an-error-gadget
                      (let ((str (if ,error-message
				     (format NIL "~A~%~%~A" condition ,error-message)
				     (format NIL "~A" condition))))
			(display-error ,an-error-gadget str)))
                    (values NIL condition))))
	  ))
    (eval ,form))
  )

;; This function will take a string like "opal:black-fill", read the symbol
;; (or expression) from the string, and then evaluate the symbol (or expr) to
;; compute its value.  If the string cannot be read, it is returned.
;;
(defun Careful-String-Eval (string
			    &optional an-error-gadget error-message)
  ;; It is an error to pass anything but a string
  (if (and (stringp string) (not (string= "" string)))
      ;; Set val to be what the user typed in
      (multiple-value-bind (val errorp)
	  (gg:Careful-Eval `(read-from-string ,string)
			   an-error-gadget error-message)
	;; Errorp could be an error condition or a number.  If it is an error
	;; condition, then there was an error while reading from the string.
	;; If it is a number, then it is the second value returned by
	;; read-from-string, which is the number of characters in the symbol
	;; (or expression) that was read from the string.  In the latter case,
	;; we are in the unfortunate situation of errorp being non-NIL even
	;; though there was no error.  Compensate with the following operation:
	(if (numberp errorp) (setq errorp NIL))
	
	(if errorp
	    ;; If couldn't even read the string, just return it with error.
	    (values string errorp)
	    ;; Now eval what the user typed in
	    (gg:Careful-Eval val an-error-gadget error-message)))
      ;; Return the bogus argument and signal an error
      (values string T)))

(defun Careful-Read-From-String (string
				 &optional an-error-gadget error-message)
  (if (and (stringp string) (not (string= "" string)))
      (multiple-value-bind (val errorp)
	  (gg:Careful-Eval `(read-from-string ,string)
			   an-error-gadget error-message)
	(if (numberp errorp) (setq errorp NIL))
	(values val errorp))
      ;; Argument was not a string, so return it with an error
      (values string T)))


(defun Careful-Eval-Formula-Lambda (expr an-error-gadget error-message
					 the-obj the-slot the-formula
					 warn-p)
  (let ((kr::*schema-self* the-obj)
	(kr::*schema-slot* the-slot)
	(kr::*current-formula* the-formula)
	(kr::*warning-on-null-link* warn-p))
    (catch 'kr::no-link
      (gg:Careful-Eval expr an-error-gadget error-message)
      )))



;;  Tell the world that error-gadget-utils has been loaded
;;
(setf (get :garnet-modules :error-gadget-utils) T)

