;;; -*- 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. ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; This file contains the parts used to create the line constrant menu
;;; and sets up the line constraint menu
;;;
;;; Changes:
;;; 14-Jul-93 AMICKISH  *white-line-style* ---> opal:white-line;  Declared
;;;                     LINE-CON-PANEL special in Line-Constraint-Do-Go.
;;; 12-May-92 ECP  Added ok and cancel buttons to *line-constraint-menu*
;;;

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

(defvar *line-constraint-menu* nil)
(defvar line-con-prim-sel-agg nil)
(defvar line-con-sec-sel-agg nil)

(defun line-constraint-do-go ()
  (declare (special LINE-CON-PANEL))
(line-constraint-do-stop)

;; model diagonal line--used to initialize the lines in the
;; line constraint menu

(create-instance 'model-diag-line opal:line
   (:x1 0) (:y1 0)
   (:x2 100) (:y2 100))

(create-instance 'LINE-CON-WIN inter:interactor-window
   (:title "line constraints")
   (:left (g-value box-constraint-win :left))
   (:top (opal:bottom box-constraint-win))
   (:width 356)
   (:height (o-formula (+ 20 (gvl :aggregate :height))))
   (:visible nil)
;   (:double-buffered-p t)
)

(create-instance '*LINE-CONSTRAINT-MENU* opal:aggregate
   (:constant '(:left :top :width :height :visible))
   (:x1-offset 0)
   (:x2-offset 0)
   (:y1-offset 0)
   (:y2-offset 0)
   (:left-offset 0)
   (:top-offset 0))

(create-instance 'LINE-CONSTRAINT-INTER inter:button-interactor
   (:maybe-constant :window :start-where)
   (:window (o-formula (gvl :operates-on :window)))
   (:active (o-formula (gvl :operates-on :parent :active)))
   (:start-where (o-formula (list :element-of (gvl :operates-on))))
   (:how-set :set))


(create-instance 'LINE-CONSTRAINT-BUTTON opal:aggregadget
   (:constant '(:width :height :visible :where-attach))
   (:floating-left (o-formula (+ (gvl :left)
				 (if (gvl :interim-selected) 4 0))))
   (:floating-top (o-formula (+ (gvl :top)
				(if (gvl :interim-selected) 4 0))))
   (:width 23) (:height 23)
   ;; become deselected whenever the user changes a selection
   (:selected (o-formula (progn (or (gv *constraint-gadget* :obj-to-constrain)
				    (gv *constraint-gadget* :obj-to-reference))
				nil)))
   (:where-attach (o-formula (nth (gvl :rank)
				  (gvl :parent :parent :where-attach))))
   (:filling-style (o-formula (gvl :parent :filling-style)))
   (:parts
    `((:shadow ,opal:rectangle
	   (:left ,(o-formula (+ 4 (gvl :parent :left))))
	   (:top ,(o-formula (+ 4 (gvl :parent :top))))
	   (:width 19) (:height 19)
	   (:filling-style ,opal:black-fill)
	   (:line-style NIL))
      (:gray-outline ,opal:rectangle
	   (:left ,(o-formula (gvl :parent :floating-left)))
	   (:top ,(o-formula (gvl :parent :floating-top)))
	   (:width 19) (:height 19)
	   (:filling-style ,opal:gray-fill))
      (:white-field ,opal:rectangle
	   (:left ,(o-formula (+ 4 (gvl :parent :floating-left))))
	   (:top ,(o-formula (+ 4 (gvl :parent :floating-top))))
	   (:width 11) (:height 11)
	   (:filling-style ,(o-formula (if (gvl :parent :selected)
					   opal:black-fill
					   opal:white-fill))))
      (:label ,opal:rectangle
	   (:visible ,(o-formula (not (gvl :parent :selected))))
	   (:left ,(o-formula (+ 7 (gvl :parent :floating-left))))
	   (:top ,(o-formula (+ 7 (gvl :parent :floating-top))))
	   (:width 5) (:height 5)
	   (:filling-style ,(o-formula (gvl :parent :filling-style)))
	   (:line-style ,(o-formula (if (equal (gvl :filling-style)
					       opal:black-fill)
					opal:white-line
					opal:default-line-style)))))))

(create-instance 'BOX-CONSTRAINT-OBJ opal:aggregadget
   (:maybe-constant :left :top :visible)
   (:constant '(:width :height :where-attach))
   (:left 0)
   (:top 0)
   (:width 123)
   (:height 123)
   (:active (o-formula (eq (gvl :parent :active) :box)))
;   (:where-attach '(:nw :w :sw :n :c :s :ne :e :se))
   (:where-attach '(0 1 2 3 4 5 6 7 8))
   (:parts
    `((:box ,opal:rectangle
	    (:left ,(o-formula (opal:gv-center-x
				(first (gvl :parent :buttons :components)))))
	    (:top ,(o-formula (opal:gv-center-y
			       (first (gvl :parent :buttons :components)))))
	    (:width 100) (:height 100)) 
      (:buttons ,opal:aggrelist
		(:constant (t :except :left :top))
		(:left ,(o-formula (gvl :parent :left)))
		(:top ,(o-formula (gvl :parent :top)))
		(:v-spacing 27) (:h-spacing 27)
		(:rank-margin 3)
		(:items 9)
		(:filling-style ,(o-formula
				  (case (gvl :parent :parent :selection)
				    (:primary opal:black-fill)
				    (t opal:white-fill))))
		(:item-prototype ,LINE-CONSTRAINT-BUTTON)
		(:interactors
		 ((:box-press ,LINE-CONSTRAINT-INTER
		      (:final-function
		       ,#'(lambda (interactor obj)
			    (s-value LINE-CON-PANEL :value nil)
			 (s-value (g-value interactor :operates-on :parent
					   :parent) 
				  :where-attach
				  (g-value obj :where-attach))
			   (ATTACH-line-CONSTRAINT interactor obj)))))))     
      (:gray-shade ,opal:rectangle
	     (:left ,(o-formula (- (gvl :parent :left) 1)))
	     (:top ,(o-formula (- (gvl :parent :top) 1)))
	     (:width ,(o-formula (+ 2 (gvl :parent :width))))
	     (:height ,(o-formula (+ 2 (gvl :parent :height))))
	     (:draw-function :and)
	     (:filling-style ,opal:gray-fill)
	     (:visible ,(o-formula (not (gvl :parent :active))))))))

(create-instance 'LINE-CONSTRAINT-OBJ opal:aggregadget
   (:maybe-constant :left :top)
   (:constant '(:visible :width :height :max-size :where-attach))
   (:left 19)
   (:top 185)
   (:width 123)
   (:height 123)
   (:max-size (o-formula (- (gvl :width) 22)))
   (:line-over nil)
   (:active (o-formula (eq (gvl :parent :active) :line)))
;   (:where-attach `(:x1 :x2))
   (:where-attach `(0 1))
   (:parts
    `((:line ,opal:line
	     (:line-over ,(o-formula (gvl :parent :line-over)))
	     (:max-size ,(o-formula (gvl :parent :max-size)))
	     (:left ,(o-formula (if (>= (gvl :line-over :width)
					(gvl :line-over :height))
				    (+ (gvl :parent :left) 11)
				    (opal:gv-center-x-is-center-of
				     (gvl :parent))) 30))
	     (:top ,(o-formula (if (>= (gvl :line-over :height)
				       (gvl :line-over :width))
				   (+ (gvl :parent :top) 11)
				   (opal:gv-center-y-is-center-of
				    (gvl :parent))) 196))
	     (:width ,(o-formula (let ((line-wd (gvl :line-over :width))
				       (line-ht (gvl :line-over :height)))
				   (if (>= line-wd line-ht)
				       (gvl :max-size)
				       (round (* (gvl :max-size)
						 (/ line-wd line-ht)))))
				 101))
	     (:height ,(o-formula (let ((line-wd (gvl :line-over :width))
					(line-ht (gvl :line-over :height)))
				   (if (>= line-ht line-wd)
				       (gvl :max-size)
				       (round (* (gvl :max-size)
						 (/ line-ht line-wd)))))
				  101))
	     (:x1 ,(o-formula (if (<= (gvl :line-over :x1)
				      (gvl :line-over :x2))
				  (gvl :left)
				  (opal:gv-right (gv :self)))
			      30))
			      
	     (:x2 ,(o-formula (if (<= (gvl :line-over :x2)
				      (gvl :line-over :x1))
				  (gvl :left)
				  (opal:gv-right (gv :self)))
			      130))
	     (:y1 ,(o-formula (if (<= (gvl :line-over :y1)
				      (gvl :line-over :y2))
				  (gvl :top)
				  (opal:gv-bottom (gv :self)))
			      196))
	     (:y2 ,(o-formula (if (<= (gvl :line-over :y2)
				      (gvl :line-over :y1))
				  (gvl :top)
				  (opal:gv-bottom (gv :self)))
			      296)))
      (:buttons ,opal:aggregadget
		(:left ,(o-formula (gvl :parent :left)))
		(:top ,(o-formula (gvl :parent :top)))
		(:width ,(o-formula (gvl :parent :width)))
		(:height ,(o-formula (gvl :parent :height)))
		(:filling-style ,(o-formula (case (gvl :parent :parent
						       :selection)
					      (:primary opal:black-fill)
					      (t opal:white-fill))))
		(:parts
		 ((:endpt1 ,LINE-CONSTRAINT-BUTTON
		       (:left ,(o-formula (- (gvl :parent :parent :line :x1)
					     11)))
		       (:top ,(o-formula (- (gvl :parent :parent :line :y1)
					    11)))
		       (:where-attach 0)
		       (:filling-style ,(o-formula (gvl :parent :filling-style))))
		  (:center ,LINE-CONSTRAINT-BUTTON
		       (:left ,(o-formula (opal:gv-center-x-is-center-of
					      (gvl :parent :parent :line))))
		       (:top ,(o-formula (opal:gv-center-y-is-center-of
					     (gvl :parent :parent :line))))
		       (:where-attach 1)
		       (:visible ,(o-formula (gvl :parent :parent 
						  :middle-button-p)))
		       (:filling-style ,(o-formula (gvl :parent :filling-style))))
		  (:endpt2 ,LINE-CONSTRAINT-BUTTON
		       (:left ,(o-formula (- (gvl :parent :parent :line :x2)
					     11)))
		       (:top ,(o-formula (- (gvl :parent :parent :line :y2)
					    11)))
		       (:where-attach ,(o-formula (if (gvl :parent :center :visible)
						      2
						      1)))
		       (:filling-style ,(o-formula (gvl :parent :filling-style))))))
		(:interactors
		 ((:line-press ,LINE-CONSTRAINT-INTER
			       (:final-function
				,#'(lambda (interactor obj)
				  (s-value LINE-CON-PANEL :value nil)
				  (s-value (g-value interactor :operates-on 
						    :parent :parent) 
					   :where-attach
					   (g-value obj :where-attach))
				  (ATTACH-line-CONSTRAINT interactor obj)))
			       ))))
      (:gray-shade ,opal:rectangle
	     (:left ,(o-formula (- (gvl :parent :left) 1)))
	     (:top ,(o-formula (- (gvl :parent :top) 1)))
	     (:width ,(o-formula (+ 2 (gvl :parent :width))))
	     (:height ,(o-formula (+ 2 (gvl :parent :height))))
	     (:draw-function :and)
	     (:filling-style ,opal:gray-fill)
	     (:visible ,(o-formula (not (gvl :parent :active))))))))

(create-instance 'LINE-CON-TITLE opal:text
   (:constant '(t))
   (:left 10)
   (:top 10)
   (:font *large-bold-italic-serif-font*)
   (:string "Line Constraint Menu"))

(opal:add-component *LINE-CONSTRAINT-MENU* LINE-CON-TITLE)

(create-instance 'LINE-CON-PRIM-SEL-AGG opal:aggregadget
;  (:maybe-constant :left :top :width :height :visible)
  (:constant '(:left :top :width :height :visible))
  (:where-attach (o-formula (progn (or (gv *constraint-gadget* :obj-to-constrain)
				       (gv *constraint-gadget* :obj-to-reference))
				   nil)))
  (:active (o-formula (case (gv *constraint-gadget* :selection-type)
			    ((one-zero one-one)
			     (if (is-a-line-p (gv *constraint-gadget*
						  :obj-to-constrain))
				 :line
			         :box))
			    (t nil))))
  (:selection :primary)
  (:left 10) 
  (:top (+ (opal:bottom line-con-title) 20))
  (:parts `((:title-frame ,titled-frame
		   (:string "obj-to-constrain")
		   (:left ,(o-formula (gvl :parent :left)))
		   (:top ,(o-formula (gvl :parent :top)))
		   (:width ,(o-formula (+ (- (opal:gv-right 
					      (gvl :parent :box))
					     (gvl :parent :line :left))
					  30)))
		   (:parts (
		     (:frame :modify
			     (:height ,(o-formula
					(+ (- (opal:bottom
					       (gvl :parent :parent :box))
					      (gvl :parent :parent :box :top))
					   20))))
		     :text-frame
		     :text)))	     
	    (:line ,line-constraint-obj
		   (:middle-button-p nil)
		   (:line-over ,(o-formula (when (gvl :active)
						 (gv *constraint-gadget*
						     :obj-to-constrain))))
		   (:left ,(o-formula (+ (gvl :parent :left) 15)))
		   (:top ,(o-formula (+ (gvl :parent :top) 17))))
	    (:box ,box-constraint-obj
		  (:left ,(o-formula (+ (opal:gv-right (gvl :parent :line))
					20)))
		  (:top ,(o-formula (+ (gvl :parent :top) 17)))))))

;; demand the value of the :line-over slot in :line so that the links
;; are set up. then store model-diag-line in :line-over so :line can
;; display itself properly
(let* ((line-agg (g-value line-con-prim-sel-agg :line))
       (line (g-value line-agg :line)))
  (g-value line-agg :line-over)
  (s-value line-agg :line-over model-diag-line)
  (g-value line :x1)
  (g-value line :x2)
  (g-value line :y1)
  (g-value line :y2)
  (g-value line-con-prim-sel-agg :where-attach))

(create-instance 'LINE-CON-SEC-SEL-AGG opal:aggregadget
  (:constant '(:left :top :width :height :visible))
  (:top (+ (opal:bottom line-con-prim-sel-agg) 20))
  (:left 10)
  (:where-attach (o-formula (progn (or (gv *constraint-gadget* :obj-to-constrain)
				       (gv *constraint-gadget* :obj-to-reference))
				   nil)))
  (:active (o-formula (case (gv *constraint-gadget* :selection-type)
			    (one-one
			     (if (is-a-line-p (gv *constraint-gadget*
						  :obj-to-reference))
				 :line
			         :box))
			    (t nil))))
  (:selection :secondary)
  (:parts `((:title-frame ,titled-frame
		   (:string "obj-to-reference")
		   (:left ,(o-formula (gvl :parent :left)))
		   (:top ,(o-formula (gvl :parent :top)))
		   (:width ,(o-formula (+ (- (opal:gv-right 
					      (gvl :parent :box))
					     (gvl :parent :line :left))
					  30)))
		   (:parts (
		     (:frame :modify
			     (:height ,(o-formula
					(+ (- (opal:bottom
					       (gvl :parent :parent :box))
					      (gvl :parent :parent :box :top))
					   20))))
		     :text-frame
		     :text)))	     
	    (:line ,line-constraint-obj
		   (:middle-button-p t)
		   (:line-over ,(o-formula (when (gvl :active)
						 (gv *constraint-gadget*
						  :obj-to-reference))))
		   (:left ,(o-formula (+ (gvl :parent :left) 15)))
		   (:top ,(o-formula (+ (gvl :parent :top) 17))))
	    (:box ,box-constraint-obj
		  (:left ,(o-formula (+ (opal:gv-right (gvl :parent :line))
					20)))
		  (:top ,(o-formula (+ (gvl :parent :top) 17)))))))

;; demand the value of the :line-over slot in :line so that the links
;; are set up. then store model-diag-line in :line-over so :line can
;; display itself properly
(let* ((line-agg (g-value line-con-sec-sel-agg :line))
       (line (g-value line-agg :line)))
  (g-value line-agg :line-over)
  (s-value line-agg :line-over model-diag-line)
  (g-value line :x1)
  (g-value line :x2)
  (g-value line :y1)
  (g-value line :y2)
  (g-value line-con-sec-sel-agg :where-attach))


(opal:add-components *LINE-CONSTRAINT-MENU* LINE-CON-PRIM-SEL-AGG
		                         LINE-CON-SEC-SEL-AGG)

;; set the interim selected slots of the buttons in the secondary line menu
;; to nil, so that they will not inherit from the prototype

(dolist (obj (g-value line-CON-SEC-SEL-AGG :line :buttons :components))
	(s-value obj :interim-selected nil))
(dolist (obj (g-value line-CON-SEC-SEL-AGG :box :buttons :components)) 
	(s-value obj :interim-selected nil))

(create-instance 'LINE-CON-PANEL garnet-gadgets:text-button-panel 
   (:constant '(t))
   (:left 10)
   (:top (+ (opal:bottom line-con-sec-sel-agg) 20))
   (:value (o-formula (progn 
			(or (gv *constraint-gadget* :obj-to-constrain)
			    (gv *constraint-gadget* :obj-to-reference))
			nil)))
   (:shadow-offset 5) (:gray-width 3)
   (:text-offset 3)
   (:final-feedback-p t)
   (:items `(("unconstrain" LINE-UNCONSTRAIN-FN) 
	     ("customize" LINE-CUSTOM-FN)))
   (:interactors `(
     (:text-button-press :modify
   	(:final-function
	 ,#'(lambda (interactor final-obj-over)
	      (let* ((action (g-value final-obj-over :action))
		     (gadget (g-value interactor :operates-on))
		     (selected (g-value final-obj-over :selected))
		     (string (when selected
			       (g-value final-obj-over :string))))
	   
		;; Propagate new selection toward :value slot
		(s-value gadget :value-obj (when selected final-obj-over))
		
		;; Actually store the new value in the value slot
		(s-value gadget :value string)

		;; Global function for all items
		(kr-send gadget :selection-function gadget string)

		;; Local function assigned to item
		(when action
		  (funcall action gadget string)))))))))

(create-instance 'LINE-CON-OK-PANEL garnet-gadgets:text-button-panel
  (:constant '(t))
  (:left (o-formula (+ (opal:gv-right line-con-title) 10)))
  (:top 10)
  (:direction :horizontal)
  (:fixed-width-p nil)
  (:button-width 44)
  (:gray-width 3) (:shadow-offset 5) (:text-offset 2)
  (:selection-function
      #'(lambda (button string)
	  (cond ((string= string "OK")
		 (s-value (g-value button :window) :visible nil))
		(t
		 (set-line-constraint-feedback)))))
  (:items '("Show Constraints" "OK"))
  (:final-feedback-p nil))

(create-instance 'X-OFFSET-BOX garnet-gadgets:labeled-box
   (:constant '(t))
   (:left (+ (opal:right LINE-CON-PANEL) 20))
   (:top (g-value LINE-CON-PANEL :top))
   (:value "0")
   (:old-value "0")
   (:numerical-value (o-formula (read-from-string (gvl :value))))
   (:label-string "x-offset")
   (:selection-function #'set-x-offset))

(create-instance 'Y-OFFSET-BOX garnet-gadgets:labeled-box
   (:constant '(t))
   (:left (g-value x-offset-box :left))
   (:top (+ (opal:bottom x-offset-box) 10))
   (:value "0")
   (:old-value "0")
   (:numerical-value (o-formula (read-from-string (gvl :value))))
   (:label-string "y-offset")
   (:selection-function #'set-y-offset))

(opal:add-components *LINE-CONSTRAINT-MENU* LINE-CON-PANEL
			LINE-CON-OK-PANEL X-OFFSET-BOX Y-OFFSET-BOX)

;; create the labeled boxes that display the coordinates of a line
(create-instance 'line-position-proto garnet-gadgets:labeled-box
   (:left (o-formula (let ((offset-right (opal:gv-right x-offset-box)))
		       (if (< offset-right 223)
			   233
			   (+ offset-right 10)))))
   (:top 0)
   (:value (o-formula (let ((selected (gv *constraint-gadget* :obj-to-constrain))
			    (slot (gvl :slot)))
			(if (and selected (is-a-line-p selected))
			    (princ-to-string (gv selected slot))
			    "0"))))
   (:selection-function #'set-position-slot)
   ;; can only change a line's position when a line is the primary or
   ;; secondary selection
   (:interactors `((:text-inter :modify
		     (:active ,(o-formula (eq (gv *constraint-gadget*
						  :selection-type)
					      'one-zero))))))
   (:slot nil))

(create-instance 'x1-box line-position-proto
   (:constant '(t))
   (:top (g-value x-offset-box :top))
   (:slot :x1)
   (:label-string "x1"))

(create-instance 'y1-box line-position-proto   
   (:constant '(t))
   (:top (+ (opal:bottom x1-box) 10))
   (:slot :y1)
   (:label-string "y1"))

(create-instance 'x2-box line-position-proto
   (:constant '(t))
   (:top (+ (opal:bottom y1-box) 20))
   (:slot :x2)
   (:label-string "x2"))

(create-instance 'y2-box line-position-proto
   (:constant '(t))
   (:top (+ (opal:bottom x2-box) 10))
   (:slot :y2)
   (:label-string "y2"))

;; gray out the position boxes when they are inactive
(create-instance 'line-pos-gray-box opal:rectangle
   (:constant '(t :except :visible))
   (:draw-function :and)
   (:left (o-formula (gv x1-box :left)))
   (:top (- (g-value x1-box :top) 2))
   (:width (o-formula (- (max (opal:gv-right x1-box)
			      (opal:gv-right y1-box)
			      (opal:gv-right x2-box)
			      (opal:gv-right y2-box))
			 (gvl :left))))
   (:height (+ (- (opal:bottom y2-box) (g-value x1-box :top)) 5))
   (:filling-style opal:gray-fill)
   (:visible nil))

(opal:add-components *line-constraint-menu* x1-box y1-box x2-box y2-box
		                         line-pos-gray-box)

(s-value LINE-CON-WIN :aggregate *line-constraint-menu*)

(opal:update LINE-CON-WIN))

(defun line-constraint-do-stop ()
  (when (boundp 'LINE-CON-WIN) (opal:destroy LINE-CON-WIN)))
