;;; -*- 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. ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; -*- Mode: Lisp; Package: LAPIDARY -*-
;;;
;;; This file and card1.lisp provides the functions and aggregadgets used 
;;; to create event cards in the interactor menus
;;;

;;; CHANGE LOG
;;;
;;; 08/24/92 Martin Sjolin - Added zerop check in PARSE

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

#|
;;;*****************************
;;; Definition of a single card
;;;*****************************

(create-instance 'card opal:aggregadget
   (:left (o-formula (+ (gvl :parent :left)
			(* (- (gvl :parent :max-item) (gvl :rank) 1) 25))))
   (:top (o-formula (+ (gvl :parent :top)
		       (* (- (gvl :parent :max-item) (gvl :rank) 1) 25))))
   (:rank 0)
   (:event-symbol (o-formula (gvl :contents :event-symbol)))
   (:on-top (o-formula (eq (gv :self) (gvl :parent :top-card))))
   (:parts
    `((:frame ,opal:rectangle
	      (:left ,(o-formula (gvl :parent :left)))
	      (:top ,(o-formula (gvl :parent :top)))
	      (:width ,(o-formula (+ (gvl :parent :contents :width) 20)))
	      (:height ,(o-formula (+ (gvl :parent :contents :height) 20)))
	      (:filling-style ,opal:white-fill))
      (:contents ,event
		 (:left ,(o-formula (+ (gvl :parent :left) 10)))
		 (:top ,(o-formula (+ (gvl :parent :top) 10)))))))

;;;**********************************************************
;;; Definition a card deck (Be sure to set the :queue slot!)
;;;**********************************************************

(create-instance 'deck opal:aggregadget
   (:left 5)
   (:top 5)
   (:queue '*QUEUE*)
   (:event-type :start-event)
   (:event-list (o-formula (let (result)
			     (gvl :components)
			     (dovalues (card (gv :self) :components)
			       (push (gv card :event-symbol) result))
			     (if (cdr result)
				 result
				 (car result)))))
   (:max-item (o-formula (length (gvl :components))))
   (:top-card (o-formula (car (last (gvl :components)))))
   (:interactors
    `((NIL ,inter:button-interactor
	   (:window ,(o-formula (gvl :operates-on :window)))
	   (:start-event :rightdown)
	   (:start-where ,(o-formula (list :element-of (gvl :operates-on))))
	   (:final-function ,#'(lambda (inter card)
			      (opal:move-component
			       (g-value inter :operates-on) card)))))))
|#

;;; translate a modifier string to individual modifier bits; the lists
;;; are kept in alphabetical order because bits-to-mod-string uses rassoc
;;; to search the list and it sorts its search list into alphabetical order

(defvar *modifier-list* '((any "any modifier")
			  (shift "shift")
			  (control "control")
			  (meta "meta")
			  (shift-control "control" "shift")
			  (shift-meta "meta" "shift")
			  (control-meta "control" "meta")
			  (shift-control-meta "control" "meta" "shift")))

(defun mod-string-to-bits (mod-string)
  (declare (special *modifier-list*))
  (cdr (assoc (read-from-string mod-string) *modifier-list*)))

(defun bits-to-mod-string (mod-bits)
  (let ((mod-entry (rassoc (sort mod-bits #'string-lessp)
			   *modifier-list* :test #'equal)))
    (if mod-entry
	(princ-to-string (car mod-entry))
        "")))

;;;
;;; this function
;;; parses an event into its modifiers (shift, control, meta, or any)
;;; and its mouse button (e.g., leftdown) or key (e.g., a)
;;;

(defun parse (event-symbol)
  (declare (special *event-info*))
  (let ((card (make-card))
	(event-string 
	 (cond ((keywordp event-symbol) (symbol-name event-symbol))
	       ((graphic-char-p event-symbol) (string event-symbol))
	       (t (char-name event-symbol))))			    
	(last-card (car (g-value *event-info* :event-cards)))
	(mod-end 0) 
	(kr::*constants-disabled* nil)
	mod-bits char)

    (cond ((string= "ANY-KEYBOARD" event-string)
	   (s-value (g-value card :any-key) :value "Any keypress:"))
	  ((string-equal "any-mousedown" event-string)
	   (s-value (g-value card :mouse) :value "any-mousedown"))
	  ((string-equal "any-mouseup" event-string)
	   (s-value (g-value card :mouse) :value "any-mouseup"))
	  (t
           ;;; separate the modifiers from the mouse button or key. Do this by
           ;;; individually searching for "any", "shift", "meta", and
	   ;;; "control". It would be nice to just look for the last '-',
	   ;;; but this does not work because some character names can have
	   ;;; dashes. Add the number of characters in these strings to a
	   ;;; running count, so that the modifiers can be stripped off
	   ;;; to yield the mouse button or character string.
	   (dolist (modifier-length '(("ANY" 4) ("SHIFT" 6)
				      ("CONTROL" 8) ("META" 5)))
		   (when (search (car modifier-length) event-string 
				 :test #'char-equal)
			 (push (car modifier-length) mod-bits)
			 (incf mod-end (second modifier-length))))

	   (s-value (g-value card :modifiers) :value mod-bits)

	   ;;; determine if this is a mouse button or a key
	   (setf char (if (zerop mod-end) (subseq event-string mod-end)
			  event-string))
	   (if (member char
		       '("leftdown" "middledown" "rightdown" "mousedown"
			 "leftup" "middleup" "rightup" "mouseup")
		       :test #'string-equal)
	       (s-value (g-value card :mouse) :value
			(string-downcase char))
	       (s-value (g-value card :key) :value char))))
    (push card (g-value *event-info* :event-cards))
    (gilt:show-in-window card 
	     (if last-card (+ (g-value last-card :window :left) 40) 100)
	     (if last-card (+ (g-value last-card :window :top) 40) 100))))
			     
#|
(defun fill-deck (deck event-list)
  (cond ((null event-list) (add-event deck))
	((atom event-list) (parse (make-card deck) event-list))
	((listp event-list) (dolist (event-symbol event-list)
			      (parse (make-card deck) event-symbol)))))

;;;======================================================================
;;;
;;; create a list of events from the cards in the event window and make
;;; the event window disappear
;;;
;;;======================================================================

(defun create-events (gadget value)
  (let ((deck (g-value gadget :parent :deck)))
    (dialog-enqueue (g-value deck :event-type)
		    (g-value deck :event-list)
		    (symbol-value (g-value deck :queue)))
    
    (mapc #'remove-card
	  (g-value gadget :parent :deck :components))
    (s-value event-win :visible nil)))
|#

(defun event-ok (gadget value)
  (declare (ignore gadget value))
  (declare (special *event-info*))
  (let (events)
    (dolist (card (g-value *event-info* :event-cards))
	    (push (make-event 
		   (g-value card :modifiers :value)
		   (or (g-value card :mouse :value)
		       (and (g-value card :any-key :value) "any-keyboard")
		       (g-value card :key :value))) 
		  events))
    (dialog-enqueue (g-value *event-info* :event-type)
		    events
		    (symbol-value (g-value *event-info* :queue)))
    ;; easy way of returning cards to free list and making them invisible
    (event-cancel nil nil)))
    

(defun event-cancel (gadget value)
  (declare (ignore gadget value))
  (declare (special *event-info* *free-cards*))
  (dolist (card (g-value *event-info* :event-cards))
	  (s-value (g-value card :window) :visible nil)
	  (push card *free-cards*))
  (s-value *event-info* :event-cards nil))

;;;======================================================================
;;;
;;; fill the cards in the event window with the appropriate information
;;; about the selected event and then display the event window
;;;
;;;======================================================================

(defun event-final-function (gadget value)
  (declare (special *event-info*))
  (let ((event-list 
	 (or (cdr (assoc value (symbol-value (g-value gadget :queue))))
		 (g-value gadget :inter value))))
    ;; place the appropriate slot-queue and event-type into *event-info*
    (s-value *event-info* :event-type value)
    (s-value *event-info* :queue (g-value gadget :queue))
    (s-value *event-info* :event-cards nil)

    ;; put the event information in the event cards
    (cond ((null event-list) (add-event))
	  ((atom event-list) (parse event-list))
	  ((listp event-list) (dolist (event-symbol event-list)
				      (parse event-symbol))))))


(defvar event-win nil)
#|
(defun make-event-window ()
  (let (event-agg)
    (create-instance 'event-win inter:interactor-window
      (:width 570)(:height 275)(:title "event window"))

    (setf event-agg (create-instance nil opal:aggregadget
      (:left 5)(:top 5)
      (:parts
       `((:ok ,garnet-gadgets:text-button
	      (:left 480)(:top 5)
	      (:string "OK")
	      (:final-feedback-p nil)
	      (:selection-function ,#'create-events))
	 (:deck ,deck
		(:left 5)(:top 50)
		(:queue ,(o-formula (gvl :window :queue)))
		(:event-type ,(o-formula (gvl :window :event-type))))))))

    ;; add event-agg to the window
    (s-value event-win :aggregate event-agg)
    (opal:update event-win)))

|#

(create-instance 'event-panel garnet-gadgets:text-button-panel
   (:left 5)
   (:top 5)
   (:inter (o-formula (gvl :window :inter)))
   (:final-feedback-p NIL)
   (:direction :horizontal)
   (:items '(:start-event :stop-event :abort-event))
   (:selection-function #'event-final-function))



