;;; -*- 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. ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; -*- Mode: Lisp; Package: GARNET-GADGETS -*-
;;;
;;; This file provides support for creating formulas. It 
;;; contains a set of functions that construct formulas which traverse
;;; an aggregate hierarchy to find an object.

(in-package "GARNET-GADGETS" :use '("LISP" "KR"))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Change Log
;;;
;;; 5/10/93 bvz Created
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; make the box constraint menu visible
(defun show-box-constraint-menu ()
  (declare (special box-constraint-win))
  (s-value box-constraint-win :visible t)
  (opal:update-all))

;;; make the line constraint menu visible
(defun show-line-constraint-menu ()
  (declare (special line-con-win))
  (s-value line-con-win :visible t)
  (opal:update-all))

;; determine whether the primary selection depends on the secondary selection

(defun depends-on-p (p-selection s-selection slot)
  (let ((value (get-value p-selection slot)))
    (when (formula-p value)
	  (dolist (link (g-formula-value value :links))
		  (when (and (eq (g-value p-selection link) s-selection)
			     ;; some links may be spuriously on the 
			     ;; formula's :links list. make sure the
			     ;; dependency holds by checking whether the
			     ;; formula is on the link's dependency list
			     (member value (kr::get-dependents p-selection
							      link)))
			(return-from depends-on-p t))))
    nil))

(defun generate-link-name (obj)
  (let ((counter 0)
	link)
    (setf link (read-from-string
		(concatenate 'simple-string ":link-" 
			     (princ-to-string counter))))
    (loop
     (when (not (has-slot-p obj link))
	   (return))
     (incf counter)
     (setf link (read-from-string
		 (concatenate 'simple-string ":link-" 
			      (princ-to-string counter)))))
    link))
  
;; if the value is an integer, return t; otherwise restore the :value slot of 
;; gadget to its old value and return nil
(defun valid-integer-p (gadget value)
  (let ((number (when (not (string= value "")) (read-from-string value))))
    (if (not (integerp number))
	(progn
	  (s-value gadget :value (g-value gadget :text-inter :original-string))
	  (constraint-gadget-error "the value must be an integer")
	  nil)
        t)))
#|
;;; =================================================================
;;; constructs a path that gvl can use to get from the source object
;;; to the destination object. this is done in three stages. first
;;; all objects on the path from the source object to the top-level 
;;; aggregate are marked. second all objects on the path from the 
;;; destination object to the common ancestor of the source and 
;;; destination objects (this will be the first marked object) are 
;;; visited and their links are pushed onto the path. third, all objects
;;; on the path from the source object to the common ancestor are visited
;;; and :parent links are pushed onto the path. this third pass continues
;;; up to the top-level aggregate and unmarks all objects on the way up
;;; =================================================================

(defun make-path (src dest)
  (let (parent path parent-path)
    ;; check if the src and dest are the same
    (when (eq src dest)
      (return-from make-path nil))
    ;; optimize for the case where the source and destination objects
    ;; are siblings (i.e., have the same parent)
    (if (eq (g-value src :parent) (g-value dest :parent))
	(setf path (list :parent (g-value dest :known-as)))
	(progn
	  ;; first pass--mark all objects between source object and top
	  ;; level aggregate
	  (setf parent src)
	  (loop 
	    (set-marked-bit parent t)
	    (setf parent (or (g-value parent :parent) 
			     (g-value parent :operates-on)))
	    (when (null parent)
	      (return)))

	  ;; second pass--push the names of all objects between the destination
	  ;; object and the source and destination objects' common ancestor
	  ;; onto the path
	  (setf parent dest)
	  (loop
	    ;; common ancestor is first marked object
	    (when (marked-p parent)
	      ;; common ancestor is made unmarked
	      (set-marked-bit parent nil)
	      (return))
	    ;; if parent is an aggrelist, the aggrelist will not have
	    ;; a pointer to this object. Create a link that will point
	    ;; to this object and push the generated link name onto the path
	    (if (is-a-p (g-value parent :parent) opal:aggrelist)
		(let* ((true-parent (g-value parent :parent)) 
		       link
		       (index (position parent 
					(g-value true-parent :components))))
		  ;; try to find a link in parent's parent that points to
		  ;; it. if none do, generate a new link
		  (setf link (dolist (l (g-value true-parent :links))
				     (when (eq (g-value true-parent l) parent)
					   (return l))))
		  (when (null link)
			(setf link (generate-link-name true-parent)))
		  (push link path)
		  (push link (g-value true-parent :links))
		  (s-value true-parent link 
			   (eval `(o-formula (nth ,index (gvl :components))))))
	        (push (g-value parent :known-as) path))
	    (setf parent (g-value parent :parent)))

	  ;; third pass--push :parent links onto the path for all objects
	  ;; between the source object and the common ancestor. then
	  ;; continue to the top-level aggregate and unmark all objects
	  ;; on the way
	  (setf parent src)
	  (loop
	    ;; common ancestor is first unmarked object
	    (when (not (marked-p parent)) 
	      (loop
		(setf parent (or (g-value parent :parent)
				 (g-value parent :operates-on)))
		(when (null parent)
		  (return))
		(set-marked-bit parent nil))
	      (return))
	    (if (g-value parent :parent)
		(push :parent parent-path)
	        (push :operates-on parent-path))
	    (set-marked-bit parent nil)
	    (setf parent (or (g-value parent :parent)
			     (g-value parent :operates-on))))
	  (append (reverse parent-path) path)))))
|#