;;; -*- Mode: LISP; Syntax: Common-Lisp; Package: LAPIDARY; 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. ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This file created by GILT V0.4: The Garnet Interface Builder
;;; on May 23, 1991, 12:31 PM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; This file uses the following objects:
;;;     TEXT-BUTTON-PANEL from package GARNET-GADGETS
;;;     MULTI-TEXT from package OPAL
;;;     RECTANGLE from package OPAL
;;;     LABELED-BOX from package GARNET-GADGETS
;;;     SCROLLING-LABELED-BOX from package GARNET-GADGETS
;;;     SCROLLING-MENU from package GARNET-GADGETS
(dolist (gadget '("scrolling-menu-loader"
		  "scrolling-labeled-box-loader"
		  "labeled-box-loader"
		  "text-buttons-loader"
		  ))
  (load (merge-pathnames gadget
			 user::Garnet-Gadgets-PathName)))

;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(setf user::*Used-Gilt-Version* "V0.4")

(in-package "LAPIDARY" :use '("LISP" "KR"))



(defvar start-where-options 
  '(T Nil :In :In-Box :In-But-Not-On 
      :Element-of :Leaf-Element-Of :List-Leaf-Element-Of 
      :List-Element-Of-Or-None :List-Leaf-Element-Of-Or-None 
      :Check-Leaf-But-Return-Element :List-Check-Leaf-But-Return-Element 
      :Check-Leaf-But-Return-Element-Or-None 
      :List-Check-Leaf-But-Return-Element-Or-None))

(defvar start-where-list-options 
  '(:List-Leaf-Element-Of 
    :List-Element-Of-Or-None 
    :List-Leaf-Element-Of-Or-None 
    :List-Check-Leaf-But-Return-Element 
    :List-Check-Leaf-But-Return-Element-Or-None))

(defvar start-where-non-type-options
  '(:in-box t nil :in-box :in-but-not-on))

(defvar *start-where-win* nil)

(defun parse-start-where (start-where)
  (declare (special start-where-gadget))
  (let* ((control (if (listp start-where) (car start-where) start-where))
	 (obj (if (listp start-where) (second start-where)))
	 (slot (if (and (listp start-where) 
			(member control start-where-list-options))
		   (third start-where))))

   (if (eq control :not-supplied)
    (progn
      (set-initial-value start-where-gadget :start-where nil)
      (s-value (g-value start-where-gadget :start-where) :selected-ranks nil)
      (set-initial-value start-where-gadget :obj "")
      (set-initial-value start-where-gadget :slot "")
      (set-gray-out-rect-visible start-where-gadget :obj-disable t)
      (set-gray-out-rect-visible start-where-gadget :slot-disable t))
    (progn
      (set-initial-value start-where-gadget :start-where (list control))
      (s-value (g-value start-where-gadget :start-where) :selected-ranks
	       (list (position control start-where-options)))
      (if obj 
	(progn
	  (set-initial-value start-where-gadget :obj 
			     (let ((kr::*print-as-structure* nil))
			       (prin1-to-string obj)))
	  (set-gray-out-rect-visible start-where-gadget :obj-disable nil))
        (progn
	  (set-gray-out-rect-visible start-where-gadget :obj-disable t)
	  (set-initial-value start-where-gadget :obj "")))
      (if slot 
	(progn
	  (set-initial-value start-where-gadget :slot
			     (symbol-name slot))
	  (set-gray-out-rect-visible start-where-gadget :slot-disable nil))
	(progn
	  (set-initial-value start-where-gadget :slot "")
	  (set-gray-out-rect-visible start-where-gadget :slot-disable t)))))))

(defun show-start-where-win (gadget value)
  (declare (ignore value))
  (declare (special start-where-gadget *start-where-win*))
  (let ((start-where (get-start-where (symbol-value (g-value gadget :queue))
				      (g-value gadget :inter)))
	(start-where-parent (g-value gadget :parent :select-box-panel)))
    (parse-start-where start-where)
    ;; gadget should not be selected yet
    (mark-as-invalid gadget :selected)
    (s-value start-where-gadget :queue (g-value gadget :queue))
    (s-value start-where-gadget :inter (g-value gadget :inter))
    (s-value start-where-gadget :start-where-parent start-where-parent)
    (multiple-value-bind (left top)
			 (opal:convert-coordinates (g-value gadget :window)
						   (g-value gadget :left)
						   (opal:bottom gadget) NIL)
    (setf *start-where-win* (gilt:show-in-window start-where-gadget 
					    left (+ 10 top) t)))))

;;; ensure that the value is a valid garnet object
(defun garnet-object-p (gadget value)
  (let* ((symbol-name (when (not (string= value ""))
			    (read-from-string value)))
	 (object (when symbol-name (eval symbol-name))))
    (if (and object (schema-p object))
	(s-value gadget :old-value value)
        (progn
	  (inter:beep)
	  (s-value gadget :value (g-value gadget :old-value))
	  (lapidary-error "Value must be the name of a garnet object")))))

;;; ensure that the value is a valid slotname
(defun garnet-slot-p (gadget value)
  (let ((symbol-name (when (not (string= value ""))
			   (read-from-string value))))
    (if (keywordp symbol-name)
	(s-value gadget :old-value value)
        (progn
	  (inter:beep)
	  (s-value gadget :value (g-value gadget :old-value))
	  (lapidary-error "Value must be a keyword")))))

;;; set the visibility of a gray out rectangle
(defun set-gray-out-rect-visible (gadget slot visible)
  (s-value (g-value gadget slot) :visible visible))

;;; enable the start-where object and slot parameters if appropriate;
;;; if an object is selected, store it in the start-where object parameter
(defun start-where-options-final-fn (gadget selection)
  (declare (ignore selection))
  (declare (special start-where-gadget))
  (let ((control (car (g-value gadget :value)))
	(obj (car (g-value *selection-info* :selected))))
    (if (keywordp control)
	(progn
	  (set-gray-out-rect-visible start-where-gadget :obj-disable nil)
	  (when obj
		(set-initial-value start-where-gadget :obj 
				   (let ((kr::*print-as-structure* nil))
				     (prin1-to-string obj))))
	  (set-gray-out-rect-visible start-where-gadget :slot-disable
			       (not (member control start-where-list-options))))
        (progn
	  (set-gray-out-rect-visible start-where-gadget :obj-disable t)
	  (set-gray-out-rect-visible start-where-gadget :slot-disable t)))))

;;; construct a start-where expression from the start-where dialog box
;;; parameters
(defun start-where-ok-fn (gadget value-list)
  (declare (special *start-where-win*))
  (let* ((control (car (value-of :start-where value-list)))
	(start-where-obj (value-of :obj value-list))
	(slot (value-of :slot value-list))
	(start-where-parent (g-value gadget :start-where-parent))
	(type (g-value start-where-parent :type-restriction))
	start-where)
    
    ;; ensure that all required parameters are present
    (when (and (not (eq control t))
	       (not (null control))
	       (string= "" start-where-obj))
	  (lapidary-error "An object must be supplied for the start-where expression")
	  (s-value *start-where-win* :visible t)
	  (return-from start-where-ok-fn))
    (when (and (listp start-where) 
	       (member control start-where-list-options)
	       (string= "" slot))
	  (lapidary-error "A slot name must be supplied for the start-where expression")
	  (s-value *start-where-win* :visible t)
	  (return-from start-where-ok-fn))
  
    ;; construct the start-where expression
    (setf start-where
	  (cond ((or (eq control t) (null control))
		 control)
		((member control start-where-list-options)
		 (list control 
		       (gg:careful-string-eval start-where-obj)
		       (read-from-string slot)))
		(t
		 (list control
		       (gg:careful-string-eval start-where-obj)))))

    ;; add the type restriction if necessary
    (if (member control start-where-non-type-options)
	(s-value start-where-parent :type nil)
        (when (g-value start-where-parent :type)
	      (setf start-where (append start-where (list :type type)))))

    (dialog-enqueue :start-where start-where 
		    (symbol-value (g-value gadget :queue)))

    ;; store the chosen start-where option in the field string slot
    ;; of the appropriate start-where gadget
    (s-value start-where-parent :value "Other")
    (s-value (g-value start-where-parent :parent :parent)
	     :field-string (symbol-name control))))

;;;*******************************************************************
;;;  TYPE-RESTRICT-FN -- add or remove type restriction from the
;;;    start-where of an interactor
;;;*******************************************************************

(defun prompt-for-type-restrict (gadget value)
  (declare (special prompt-gadget prompt-window))
  (let* ((queue (g-value gadget :queue))
	(start-where (g-value gadget :parent :select-box-panel))
	(type-restriction (g-value start-where :type-restriction)))
    ;; if a type restriction should be added, prompt the user for
    ;; the type restriction and store it in the type restriction
    ;; field of the start-where aggregate. Also push the new start-where
    ;; onto the queue if the value and field-string fields in the
    ;; start-where agg are set
    (if value
	(let* ((start-where-value (get-start-where (symbol-value queue)
						   (g-value gadget :inter)))
	       (control (if (listp start-where-value)
			    (car start-where-value)
			    start-where-value)))
	  (when (or (eq control t) (null control)
		    (eq control :in) (eq control :in-box) 
		    (eq control :in-but-not-on))
	    (lapidary-error
	     (format nil "cannot specify a type restrictor for a start-where that begins with ~S" control))
	    (s-value start-where :type nil)
	    (s-value gadget :value nil)
	    (return-from prompt-for-type-restrict))
	  
	  ;; set the type restriction back to nil; until a type restriction
	  ;; is actually entered, the type field should stay nil
	  (s-value gadget :value nil)

	  ;; get the type restriction
	  (s-value prompt-gadget :function-for-ok 'type-restrict-fn)
	  (let* ((kr::*print-as-structure* nil)
		 (type-string (if type-restriction
				  (prin1-to-string type-restriction)
				  "")))
	    ;; lisp won't convert a list to the appropriate string notation
	    ;; so we must do it 
	    (when (and type-restriction (listp type-restriction))
		  (setf type-string (concatenate 'string "(list " (string-left-trim "(" type-string))))
	    (set-initial-value prompt-gadget :result type-string))
			       
	  (s-value prompt-gadget :string
		   "Please enter a type restriction. A type restriction can
either be a single object (e.g., opal:circle) or
a list of objects (e.g., (list opal:circle opal:line)).
If you enter a list, it should be an executable list
expression, such as (list ...) or `(...)")
	  (s-value prompt-gadget :calling-gadget gadget)
	  (multiple-value-bind (left top)
			 (opal:convert-coordinates (g-value gadget :window)
						   (g-value gadget :left)
						   (opal:bottom gadget) NIL)
			 (setf prompt-window (gilt:show-in-window prompt-gadget
					      left top t))))
	  
	; else the type restriction should be removed--get the start where
	; and chop off the type information using subseq
	(let ((start-where-value (get-start-where (symbol-value queue)
				     (g-value gadget :inter))))
	  (when (listp start-where-value)
		(dialog-enqueue :start-where 
				(subseq start-where-value 0 
					(position :type start-where-value))
				(symbol-value queue)))
	  (s-value start-where :type value)))))

(defun type-restrict-fn (gadget value-list)
  (declare (special prompt-window))
  (let* ((type-restriction (value-of :result value-list))
	 (calling-gadget (g-value gadget :calling-gadget))
	 (queue (g-value calling-gadget :queue))
	 (inter (g-value calling-gadget :inter))
	 (start-where-value (get-start-where (symbol-value queue) inter))
	 (start-where-gadget (g-value calling-gadget :parent :select-box-panel)))
    
    (when (string= type-restriction "")
	  (lapidary-error 
	   "Type restriction cannot be an empty string. 
Press cancel if you do not want a type restriction")
	  (s-value prompt-window :visible t)
	  (return-from type-restrict-fn))

    (setf type-restriction (eval (read-from-string type-restriction)))
    
    ;; add the type restriction to the start-where
    (dialog-enqueue :start-where 
		    (append (subseq start-where-value 
				    0 (position :type start-where-value))
			    (list :type type-restriction))
		    (symbol-value queue))
	
    ;; add the type restriction information to the start-where aggregate
    (s-value start-where-gadget :type t)
    (s-value start-where-gadget :type-restriction type-restriction)))

(defun insert-inter-into-agg-query-fn (gadget value)
  (cond ((string= value "highlight aggregate")
	 (garnet-debug:flash (g-value gadget :parent :agg-to-insert-into)))
	(t
	 (s-value (g-value gadget :window) :visible nil)
	 (opal:update-all)
	 (inter:interaction-complete (string= value "ok")))))

(create-instance 'START-WHERE-GADGET OPAL:AGGREGADGET
  (:WINDOW-LEFT 0)
  (:WINDOW-TOP 0)
  (:WINDOW-WIDTH 384)
  (:WINDOW-HEIGHT 261)
  (:FUNCTION-FOR-OK 'start-where-ok-fn)
  (:EXPORT-P T)
  (:WINDOW-TITLE "Start-Where-Win")
  (:PACKAGE-NAME "LAPIDARY")
  (:LEFT 0)
  (:TOP 0)
  (:WIDTH (o-formula (GVL :WINDOW :WIDTH) 384))
  (:HEIGHT (o-formula (GVL :WINDOW :HEIGHT) 261))
  (:parts `(
    (:start-where ,GARNET-GADGETS:SCROLLING-MENU
      (:GILT-REF "TYPE-SCROLLING-MENU")
      (:INDICATOR-FONT ,(create-instance nil OPAL:FONT
            (:SIZE :SMALL)))
      (:PAGE-INCR 5)
      (:SCR-INCR 1)
      (:SCROLL-SELECTION-FUNCTION NIL)
      (:MENU-SELECTION-FUNCTION start-where-options-final-fn)
      (:H-ALIGN :LEFT)
      (:SCROLL-ON-LEFT-P NIL)
      (:MIN-FRAME-WIDTH 0)
      (:TEXT-OFFSET 4)
      (:FINAL-FEEDBACK-P T)
      (:INT-MENU-FEEDBACK-P T)
      (:MULTIPLE-P nil)
      (:ITEM-FONT ,OPAL:DEFAULT-FONT)
      (:TITLE-FONT ,(create-instance nil OPAL:FONT
            (:SIZE :LARGE)
            (:FAMILY :SERIF)))
      (:V-SPACING 6)
      (:MIN-SCROLL-BAR-WIDTH 20)
      (:INDICATOR-TEXT-P NIL)
      (:PAGE-TRILL-P T)
      (:SCR-TRILL-P T)
      (:NUM-VISIBLE 5)
      (:INT-SCROLL-FEEDBACK-P NIL)
      (:TITLE "Start Where")
      (:ITEMS (T Nil :In :In-Box :In-But-Not-On :Element-of :Leaf-Element-Of :List-Leaf-Element-Of :List-Element-Of-Or-None :List-Leaf-Element-Of-Or-None :Check-Leaf-But-Return-Element :List-Check-Leaf-But-Return-Element :Check-Leaf-But-Return-Element-Or-None :List-Check-Leaf-But-Return-Element-Or-None))
      (:item-to-string-function
       ,#'(lambda (item)
	   (if item
	       (if (stringp item)
		   item
		   (string-capitalize (string-trim ":" item)))
	       "Nil")))
      (:BOX (30 50 111 145 ))
      (:LEFT ,(o-formula (FIRST (GVL :BOX)) 30))
      (:TOP ,(o-formula (SECOND (GVL :BOX)) 50)))
    (:obj ,GARNET-GADGETS:SCROLLING-LABELED-BOX
      (:GILT-REF "TYPE-SCROLLING-LABELED-BOX")
      (:MIN-FRAME-WIDTH NIL)
      (:FIELD-FONT ,OPAL:DEFAULT-FONT)
      (:FIELD-OFFSET 2)
      (:LABEL-OFFSET 5)
      (:LABEL-FONT ,(create-instance nil OPAL:FONT
            (:FACE :BOLD)))
      (:BOX (30 200 322 18 ))
      (:MIN-WIDTH 20)
      (:GROW-P T)
      (:selection-function garnet-object-p)
      (:LABEL-STRING "Start Where Object")
      (:LEFT ,(o-formula (FIRST (GVL :BOX)) 30))
      (:TOP ,(o-formula (SECOND (GVL :BOX)) 200))
      (:WIDTH ,(o-formula (THIRD (GVL :BOX)) 322)))
    (:slot ,GARNET-GADGETS:LABELED-BOX
      (:GILT-REF "TYPE-LABELED-BOX")
      (:LABEL-FONT ,(create-instance nil OPAL:FONT
            (:FACE :BOLD)))
      (:LABEL-OFFSET 5)
      (:MIN-FRAME-WIDTH 120)
      (:selection-function garnet-slot-p)
      (:FIELD-FONT ,OPAL:DEFAULT-FONT)
      (:FIELD-OFFSET 6)
      (:BOX (30 230 97 18 ))
      (:MIN-WIDTH 20)
      (:LABEL-STRING "Slot")
      (:LEFT ,(o-formula (FIRST (GVL :BOX)) 30))
      (:TOP ,(o-formula (SECOND (GVL :BOX)) 230)))
    (:obj-disable ,OPAL:RECTANGLE
      (:GILT-REF "TYPE-RECTANGLE")
      (:DRAW-FUNCTION :AND)
      (:FILLING-STYLE ,OPAL:GRAY-FILL)
      (:LINE-STYLE ,OPAL:DEFAULT-LINE-STYLE)
      (:BOX (25 196 335 30 ))
      (:GROW-P T)
      (:LEFT ,(o-formula (FIRST (GVL :BOX)) 25))
      (:TOP ,(o-formula (SECOND (GVL :BOX)) 196))
      (:WIDTH ,(o-formula (THIRD (GVL :BOX)) 335))
      (:HEIGHT ,(o-formula (FOURTH (GVL :BOX)) 30)))
    (:title ,OPAL:MULTI-TEXT
      (:GILT-REF "TYPE-TEXT")
      (:FONT ,(create-instance nil OPAL:FONT
            (:SIZE :VERY-LARGE)
            (:FACE :BOLD-ITALIC)
            (:FAMILY :SERIF)))
      (:BOX (10 10 3 3 ))
      (:STRING "Select Start Where")
      (:LEFT ,(o-formula (FIRST (GVL :BOX)) 10))
      (:TOP ,(o-formula (SECOND (GVL :BOX)) 10)))
    (:slot-disable ,OPAL:RECTANGLE
      (:GILT-REF "TYPE-RECTANGLE")
      (:GROW-P T)
      (:BOX (27 229 160 22 ))
      (:LINE-STYLE ,OPAL:DEFAULT-LINE-STYLE)
      (:FILLING-STYLE ,OPAL:GRAY-FILL)
      (:DRAW-FUNCTION :AND)
      (:LEFT ,(o-formula (FIRST (GVL :BOX)) 27))
      (:TOP ,(o-formula (SECOND (GVL :BOX)) 229))
      (:WIDTH ,(o-formula (THIRD (GVL :BOX)) 160))
      (:HEIGHT ,(o-formula (FOURTH (GVL :BOX)) 22)))
    (:stop-group ,GARNET-GADGETS:TEXT-BUTTON-PANEL
      (:SELECTION-FUNCTION GILT:OKCANCEL-FUNCTION)
      (:GILT-REF "TYPE-OKCANCEL")
      (:ITEMS ("OK" "Cancel" ))
      (:GRAY-WIDTH 3)
      (:FINAL-FEEDBACK-P NIL)
      (:TEXT-OFFSET 2)
      (:SHADOW-OFFSET 5)
      (:DIRECTION :HORIZONTAL)
      (:BOX (229 10 124 21 ))
      (:LEFT ,(o-formula (FIRST (GVL :BOX)) 229))
      (:TOP ,(o-formula (SECOND (GVL :BOX)) 10))))))

(create-instance 'INTER-AGG-QUERY OPAL:AGGREGADGET
  (:FUNCTION-FOR-OK NIL)
  (:PACKAGE-NAME "LAPIDARY")
  (:LEFT 0)
  (:TOP 0)
  (:HEIGHT (o-formula (GVL :WINDOW :HEIGHT) 225))
  (:WINDOW-LEFT 0)
  (:WINDOW-TOP 0)
  (:WINDOW-WIDTH 610)
  (:WINDOW-HEIGHT 225)
  (:WIDTH (o-formula (GVL :WINDOW :WIDTH) 610))
  (:WINDOW-TITLE "inter-agg-query")
  (:EXPORT-P T)
  (:parts `(
    (0 ,OPAL:multi-TEXT
      (:BOX (100 19 3 3 ))
      (:CONSTANT T)
      (:string ,(o-formula 
	   (format nil "It appears that this interactor should be inserted into
the aggregate named ~S.

-If you would like to see this aggregate highlighted, 
   press the 'highlight aggregate' button.
-If you would like the interactor inserted into the
   aggregate, press the 'insert into aggregate' button.
-If you do not want the interactor inserted into the
   aggregate, press the cancel button." (gvl :parent :agg-to-insert-into))))
      (:LEFT ,(o-formula (FIRST (GVL :BOX)) 100))
      (:TOP ,(o-formula (SECOND (GVL :BOX)) 19)))
    (1 ,GARNET-GADGETS:TEXT-BUTTON-PANEL
      (:BOX (28 171 535 44 ))
      (:CONSTANT T)
      (:final-feedback-p nil)
      (:LEFT ,(o-formula (FIRST (GVL :BOX)) 28))
      (:ITEMS ("highlight aggregate" "ok" "cancel" ))
      (:selection-function insert-inter-into-agg-query-fn)
      (:DIRECTION :HORIZONTAL)
      (:TOP ,(o-formula (SECOND (GVL :BOX)) 171))))))



	 
	 