;; -*- Mode:Lisp; Package:CLIO-OPEN; Base:10; Lowercase:T; Syntax:Common-Lisp -*-


;;;----------------------------------------------------------------------------------+
;;;                                                                                  |
;;;                          TEXAS INSTRUMENTS INCORPORATED                          |
;;;                                  P.O. BOX 149149                                 |
;;;                                AUSTIN, TEXAS 78714                               |
;;;                                                                                  |
;;;             Copyright (C) 1989, 1990 Texas Instruments Incorporated.             |
;;;                                                                                  |
;;; Permission is granted to any individual or institution to use, copy, modify, and |
;;; distribute this software, provided that  this complete copyright and  permission |
;;; notice is maintained, intact, in all copies and supporting documentation.        |
;;;                                                                                  |
;;; Texas Instruments Incorporated provides this software "as is" without express or |
;;; implied warranty.                                                                |
;;;                                                                                  |
;;;----------------------------------------------------------------------------------+
;;;
;;;  Implementation Strategy:
;;;
;;;  
;;;  A confirm is invoked by a originating contact (near). A triangular shadow originating 
;;;  from the "near" contact is drawn into the root with a given quadrant gravity, which
;;;  is dependent on the position of the originating contact. After a response is given
;;;  to confirm the area overshadowed by the confirm's shadow is refreshed over two rectangular
;;;  areas covering the overshadowed area. The sensitivity of the originating contact is turned
;;;  off when a confirm is invoked and turned back on when confirm receives a response.
;;;  


(in-package "CLIO-OPEN")

(export '(
	  confirm
	  confirm-accept-label
	  confirm-accept-only
	  confirm-cancel-label
	  confirm-message
	  confirm-near
	  confirm-p
	  make-confirm
	  ))

;; OL GUI spec for the apex of the confirm, scale-dependent distance from the originating contact)
(defconstant  *confirm-apex-dimensions* (list :small 36 :medium 42 :large 50 :extra-large 64))

(defconstant  *confirm-shadow-images*
	      (list 
		:north-west (list :upper 12%gray :lower 25%gray)
		:north-east (list :upper 12%gray :lower 25%gray)
		:south-west (list :upper 25%gray :lower 50%gray)
		:south-east (list :upper 25%gray :lower 50%gray)
		))


;; Confirm scale is one scale larger than near's scale
(defconstant *scales* '(:small :medium :large :extra-large :extra-large))

;;;----------------------------------------------------------------------------+
;;;    Utility  Functions                                                      +
;;;                                                                            +
;;;----------------------------------------------------------------------------+


(defun quadrant-gravity (x y root)
  (let* ((xc (pixel-round (contact-width root) 2))
	 (yc (pixel-round (contact-height root) 2))
	 (north (< y yc))
	 (west  (< x xc))
	 )
    (if north
	(if west
	    :north-west
	    :north-east)
	(if west
	    :south-west
	    :south-east))))

(defun find-confirm-sheet (confirm)
  (car (composite-children confirm)))

;;;----------------------------------------------------------------------------+
;;;                                                                            +
;;;  Confirm-SHEET contact                                                     +
;;;                                                                            +
;;;----------------------------------------------------------------------------+

(defcontact confirm-sheet  (core composite)
  ()
  (:resources
    (background :initform :parent-relative)
    (event-mask :initform #.(make-event-mask :exposure)))
  (:documentation "The actual container for confirm component areas."))

;;;----------------------------------------------------------------------------+
;;;                                                                            +
;;;  CONFIRM contact                                                           +
;;;                                                                            +
;;;----------------------------------------------------------------------------+

(defcontact confirm (core core-shell override-shell)
  ((near         :initform nil
		 :type (or null contact)
		 :initarg :near
		 :accessor confirm-near
		 :documentation "Indicating point or contact of origination")

   (cancel-label :initform "Cancel"
		 :type     string
		 :accessor confirm-cancel-label
		 :initarg  :cancel-label)
   
   ;; Internal slots
   (points       :type           (vector window) ;; storage x-near y-near & shadow regions
	 	 :initform       (make-array 6))
   (previous-pointer-x
                    :type (or null int16)
		    :initform nil)
   (previous-pointer-y
                    :type (or null int16)
		    :initform nil)
   (control-default :type (or null contact)
		    :initform nil))
  (:resources
    (save-under :initform :on)
    (default-control :initform :accept :type (member :accept :cancel))
    (accept-label    :type string :initform "OK")
    cancel-label
    (border-width    :initform 1) 
    (accept-only     :type (member :on :off) :initform :off)
    (message         :initform "Press a button to continue."))
  (:documentation "A dialog which presents a simple message."))

(defmethod (setf contact-foreground) :after (new-value (self confirm))
  (setf (contact-foreground (car (composite-children self))) new-value))


;; Index values for accessing x-near y-near
(defconstant *x-near*       0)
(defconstant *y-near*       1)


(defun make-confirm (&rest initargs)
  "Creates and returns a confirm instance."
  (declare (values confirm)) 
  (apply #'make-contact 'confirm initargs))


;;;----------------------------------------------------------------------------+
;;;                                                                            +
;;; Accessors                                                                  +
;;;                                                                            +
;;;----------------------------------------------------------------------------+

(defun find-accept-button (confirm)
  (find :accept (composite-children (find-confirm-sheet confirm)) :key 'contact-name))

(defun find-cancel-button (confirm)
  (find :cancel (composite-children (find-confirm-sheet confirm)) :key 'contact-name))

(defun find-message-area (confirm)
  (find :message (composite-children (find-confirm-sheet confirm)) :key 'contact-name))

(defmethod dialog-default-control ((self confirm))
  (with-slots (control-default) self
   (contact-name control-default)))

(defmethod (setf dialog-default-control) (new-value (confirm confirm))
  (check-type new-value (member :accept :cancel) "one of :ACCEPT or :CANCEL")
  (assert (or (eq new-value :accept) (eq (confirm-accept-only confirm) :off)) nil
	  "No cancel control exists for ~a." confirm)
  (with-slots (control-default) confirm
    (when control-default
      (setf (choice-item-highlight-default-p control-default) nil))
    (setf control-default
	  (find new-value (composite-children (find-confirm-sheet confirm)) :key #'contact-name))
    (setf (choice-item-highlight-default-p control-default) t)
    new-value))


(defmethod confirm-accept-only ((self confirm))
  (let ((cancel-button (find-cancel-button self)))
    (if (and cancel-button (eq :mapped (contact-state cancel-button)))
	:off
	:on)))


(defmethod (setf confirm-accept-only) (value (self confirm))
  "Set confirm's cancel button to the appropriate setting depending on VALUE.
   create the buttons if necessary."
  (check-type value switch "one of :ON or :OFF")
  (let* ((sheet (find-confirm-sheet self))
	 (cancel-button (find-cancel-button self)))
    (if cancel-button
	(setf (contact-state cancel-button)
	      (if (eq value :on) :withdrawn :mapped))

	(when (eq value :off)
	  (with-slots (cancel-label) self
	    (add-callback (make-action-button :parent sheet :name :cancel :label cancel-label)
			  :release 'dialog-cancel self)))))
  value)


(defmethod confirm-message ((self confirm))
  (display-text-source (find-message-area self)))

 (defmethod (setf confirm-message) (string (self confirm))
  (setf (display-text-source (find-message-area self)) string))

(defmethod confirm-accept-label ((self confirm))
  (button-label (find-accept-button self)))

(defmethod (setf confirm-accept-label) (string (self confirm))
  (setf (button-label (find-accept-button self)) string))

(defmethod confirm-cancel-label ((self confirm))
  (button-label (find-cancel-button self)))

(defmethod (setf confirm-cancel-label) :after (string (self confirm))
  (let ((label  (find-cancel-button self)))
    (when label (setf (button-label label) string))))


;;;----------------------------------------------------------------------------+
;;;                                                                            |
;;;                            Initialization                                  |
;;;                                                                            |
;;;----------------------------------------------------------------------------+

(defmethod initialize-instance :after ((self confirm) &key message accept-only accept-label 
					(default-control :accept) &allow-other-keys)
  (with-slots (x y width height near foreground scale)  self
    (unless near (setq near self))

    ;; Create the sheet
    (let* ((sheet (make-contact 'confirm-sheet :name :sheet
				:parent self
				:x 0 :y 0
				:width width :height height
				:border-width 0))
	   (near-scale (contact-scale near)))

      (setf scale (nth (1+ (position near-scale *scales*)) *scales*))
      ;; Create the message area

      (make-display-text :name :message
			 :parent sheet
			 :source message
			 :alignment :center
			 :x 0 :y 0
			 :border-width 0)

      ;; Create buttons for command area
      (add-callback (make-action-button :parent sheet :name :accept :label accept-label)
		    :release 'dialog-accept self)
      
      ;; Initialize cancel control if necessary
      (setf (confirm-accept-only self) accept-only)
            
      
      (setf (dialog-default-control self) default-control))))


;;;----------------------------------------------------------------------------+
;;;                                                                            +
;;; Dialog                                                                     +
;;;                                                                            +
;;;----------------------------------------------------------------------------+
	

(defmethod dialog-accept ((self confirm))
  "Invokes :accept callback function and pops down the dialogue"
  (setf (contact-state self) :withdrawn)
  (apply-callback self :accept)
  )

(defmethod dialog-cancel ((self confirm))
  "Invokes :cancel callback function and pops down the dialogue."
  (setf (contact-state self) :withdrawn)
  (apply-callback self :cancel)
  )



;;;----------------------------------------------------------------------------+
;;;                                                                            +
;;; Confirm Map : where real work happens                                      +
;;;                                                                            +
;;;----------------------------------------------------------------------------+


;; If the pointer moves off the Confirm don't warp pointer to Near just leave
;; where the Confirm action button was selected otherwise warp pointer to Near after
;; selecting a Confirm action button.

;; Track the state of pointer position w.r.t Confirm by storing state in internal slot 
;; of Confirm (ie. Did it stay on the Confirm the whole time or did it move off the Confirm?).


(defun calculate-upper-shadow-vertices (points x y gravity right-edge bottom-edge)
  "Determine the two sets of points for drawing the upper triangular shadow"
  (case gravity
    (:north-east
     (setf (svref points 2) x (svref points 3) y (svref points 4) (+ 3 right-edge) (svref points 5)  y))
    (:north-west
     (setf (svref points 2) x (svref points 3) y (svref points 4) right-edge (svref points 5)  (1- y)))
    (:south-west
     (setf (svref points 2) x (svref points 3) y (svref points 4) x (svref points 5) (+ bottom-edge 1)))
    (:south-east
     (setf (svref points 2) (+ right-edge 2)
	   (svref points 3) (+ 2 bottom-edge) (svref points 4) (+ right-edge 2) (svref points 5) y))))
   
(defun calculate-lower-shadow-vertices (points x y gravity right-edge bottom-edge)
  "Determine the two sets of points for drawing the lower triangular shadow"
  (case gravity
    (:north-east
     (setf (svref points 2) (+ right-edge 2)
	   (svref points 3) y (svref points 4) (+ right-edge 2) (svref points 5) bottom-edge))
    (:north-west
     (setf (svref points 2) x (svref points 3) (1- y) (svref points 4) x (svref points 5) bottom-edge))
    (:south-west
     (setf (svref points 2) (1- x)
	   (svref points 3) (+ bottom-edge 1) (svref points 4) right-edge (svref points 5) (+ bottom-edge 2)))
    (:south-east
     (setf (svref points 2) x
	   (svref points 3) (+ 2 bottom-edge) (svref points 4) (+ 2 right-edge) (svref points 5) (+ 2  bottom-edge)))))
   
(defun draw-confirm-triangular-shadows (confirm root x y width height points gravity)
  "Draw two triangular shadows originating from NEAR given the calculated vertices"
  (proclaim '(inline calculate-shadows-vertices ))
  (let*
      ((images (getf *confirm-shadow-images* gravity))
       (upper-image (getf images :upper))
       (lower-image (getf images :lower))
       (bottom-edge  (+ y height))
       (right-edge   (+ x width)))
    (calculate-upper-shadow-vertices points x y gravity right-edge bottom-edge)
    (using-gcontext
     (gcontext :drawable root
	       :background (contact-current-background-pixel confirm)
	       :foreground (screen-black-pixel (contact-screen root))
	       :fill-style :opaque-stippled
	       :stipple    (contact-image-mask root upper-image :depth 1)
	       :subwindow-mode :include-inferiors
	       )	  
     (draw-lines root gcontext points :fill-p t :shape :complex)
     (calculate-lower-shadow-vertices points x y gravity right-edge bottom-edge)
     (with-gcontext (gcontext :stipple (contact-image-mask root lower-image :depth 1))
       (draw-lines root gcontext points :fill-p t :shape :complex)))))
  
(defmethod shell-mapped ((self confirm))
  "Recomputes x and y given NEAR and invokes :initialize callback function."    
  (with-slots (near height width points previous-pointer-x previous-pointer-y control-default) 
    self
    (unless (eq self near)
      (multiple-value-bind (x-near y-near)
	  (contact-translate near
			     (pixel-round (contact-width near) 2);; Use center point of near
			     (pixel-round (contact-height near) 2))
	(setf (svref points *x-near*) x-near)
	(setf (svref points *y-near*) y-near)
	(let* ((root (contact-root self))
	       (gravity (quadrant-gravity x-near y-near root))
	       (apex    (getf *confirm-apex-dimensions* (contact-scale self)))
	       (root-width  (contact-width root))
	       (root-height (contact-height root)))

	  ;; Set Confirm's X and Y w.r.t originating contact
	  (multiple-value-bind (x y)
	      (case gravity
		(:north-east
		 (values (- x-near apex width)
			 (+ y-near apex)))
		(:north-west
		 (values (+ x-near apex)
			 (+ y-near apex)))
		(:south-west
		 (values (+ x-near apex)
			 (- y-near apex height)))
		(:south-east
		 (values (- x-near apex width)
			 (- y-near apex height))))
	      
	    ;; If CONFIRM will be clipped, compensate
	    ;; and adjust x and y of CONFIRM
	    (let ((adjusted-x (min (max x 0) (- root-width width)))
		  (adjusted-y (min (max y 0) (- root-height height))))
	      (change-geometry self
			       :x adjusted-x 
			       :y adjusted-y 
			       )
	      ;; Turn near's sensitivity off
	      (setf (contact-sensitive near) :off))))))
      
    (apply-callback self :map)
    (apply-callback self :initialize)

    ;; Store position for pointer unwarping later....
    (multiple-value-setq
	(previous-pointer-x previous-pointer-y) (pointer-position self))
      
    (warp-pointer
     control-default
     (pixel-round (contact-width control-default)  2)
     (- (contact-height control-default) 2))))
  

(defmethod display ((manager confirm-sheet)
		    &optional exposed-x exposed-y exposed-width exposed-height &key)
  (declare (ignore exposed-x exposed-y exposed-height exposed-width))
  (proclaim '(inline draw-confirm-triangular-shadows))
    
  (with-slots (width height x y points) 
    (contact-parent manager)
    (let ((root (contact-root manager)))
      (draw-confirm-triangular-shadows
       manager root
       x  y width height points
       (quadrant-gravity (svref points *x-near*) (svref points *y-near*) root))))

  (with-slots (width height foreground)
    manager
    (using-gcontext (gcontext :drawable manager :foreground foreground :Subwindow-mode :include-inferiors)
		    (draw-rectangle manager gcontext 3 3 (- width 7) (- height 7)))))



(defevent confirm :leave-notify pointer-off-confirm)

(defmethod pointer-off-confirm ((self confirm))
  (with-slots (previous-pointer-x) self
    (setf previous-pointer-x nil)))



(defun calculate-reexposed-areas (confirm root)
  "Determine two rectangular areas encompassing the triangular shadows drawn by confirm"
  (with-slots (x y width height near points)
    confirm
    (let* (
	   (apex (getf *confirm-apex-dimensions* (contact-scale confirm)))
	   (x-near      (svref points *x-near*))
	   (y-near      (svref points *y-near*))
	   (right-edge  (+ x width))
	   (bottom-edge (+ y height))
	   (gravity (quadrant-gravity x-near y-near root))
	   )
      (case gravity
	(:north-east
	 (values
	  x           (- y apex) width apex
	  right-edge (- y apex) apex  (+ height apex))
	 )
	(:north-west
	 (values
	  x-near y-near     apex   (+ height apex)
	  x     (- y apex)  width  apex)
	 )
	(:south-west
	 (values
	  (- x apex) y           apex  (+ height apex)
	  x         bottom-edge width apex)
	 )
	(:south-east
	 (values 
	  x           bottom-edge width apex
	  right-edge	 y           apex  (+ height apex)))))))
   
(defun reexpose-overshadowed-area (confirm root near)
  "Refresh the root area that confirm overshadowed"
  (proclaim '(inline calculate-reexposed-areas))
  (multiple-value-bind (area1-x area1-y area1-width area1-height
				area2-x area2-y area2-width area2-height)
      (calculate-reexposed-areas confirm root)
    (refresh root :x area1-x :y area1-y :width area1-width :height area1-height)
    (with-slots (sensitive) near
		(setq sensitive :on))
    (refresh root :x area2-x :y area2-y :width area2-width :height area2-height)))

(defmethod shell-unmapped :before ((self confirm))
  (proclaim '(inline reexpose-overshadowed-area))
  (with-slots (points near previous-pointer-x previous-pointer-y)
    self
    (unless (eq self near)
      ;; Erase shadow.
      (reexpose-overshadowed-area self (contact-root self) near)
	
      ;; Unwarp pointer to original position, if necessary.
      (when previous-pointer-x
	(warp-pointer self previous-pointer-x previous-pointer-y)))))


;;;----------------------------------------------------------------------------+
;;;                                                                            +
;;;  Geometry Management                                                       +
;;;                                                                            +
;;;----------------------------------------------------------------------------+
 
(defmethod change-layout ((self confirm-sheet) &optional newly-managed)
  ;;The idea here is to make the accept and cancel buttons be separated by the
  ;;standard horizontal spacing, and then centered within the sheet.  The standard 
  ;;vertical spacing will be enforced between the bottom edge of the taller button
  ;;and the edge of the message.
  ;;Force the message area to be the smaller of its preferred size or the space remaining
  ;;(allowing for horizontal/vertical margins).  Center it within the remaining space.
  (declare (ignore newly-managed))
  (with-slots (width height children parent) self
    (let* ((accept-button (find-accept-button parent))
	   (cancel-button (find-cancel-button parent))
	   (message-area  (find-message-area  parent))
	   (abw (contact-border-width accept-button))
	   (awidth (+ abw abw (contact-width accept-button)))
	   (aheight (+ abw abw (contact-height accept-button)))
	   (screen (contact-screen self))
	   (pixel (getf *dialog-point-spacing* (contact-scale self)))
	   (hspace (point-pixels screen pixel :horizontal))	
	   (vspace (point-pixels screen pixel :vertical))
	   rbw rwidth rheight button-x button-y) 

      ;;Figure out where buttons should go.  Make their top edges align.
      (if (eq (confirm-accept-only (contact-parent self)) :on)
	  (progn
	    (setf button-y (- height aheight vspace)
		  button-x (floor (- width awidth) 2))
	    (move accept-button  button-x button-y)
	    )
	  (progn 
	    (setf rbw (contact-border-width cancel-button)
		  rwidth (+ rbw rbw (contact-width cancel-button))
		  rheight (+ rbw rbw (contact-height cancel-button))
		  button-y (- height (+ (max aheight rheight) vspace 3))       
		  button-x (floor (- width (+ awidth rwidth hspace 3)) 2))
	    (with-state (accept-button)
	      (move accept-button  button-x  button-y)
	      )
	    (incf button-x (+ awidth hspace))
	    (with-state (cancel-button)
	      (move cancel-button button-x button-y)
	      )
	    )
	  )

      (IF (or (zerop width) (zerop height) )			; not initialized...
	  (multiple-value-bind (p-width p-height)
	      (preferred-size self)
	    (change-geometry self :width p-width :height p-height :accept-p t))
	  ;; else...
	  
	  ;;Make message-area fit within space remaining
	  (with-state (message-area)
	    (let ((new-width  (max 1
				   (- width hspace hspace)
				   ))
		  (new-height (max 1 (- button-y vspace vspace)))
		  )
	      (resize message-area
		      new-width 	;;use 1 as a lower bound to prevent
		      new-height	;;width/height sizing errors
		      0)
	      ;;Center message-area within space remaining.
	      ;;Don't have to worry about it's border-width since it's guaranteed
	      ;;to be zero by the previous call to RESIZE.
	      (move message-area
		    (max hspace (floor (- width (contact-width message-area)) 2))
		    (max vspace (floor (- (contact-y accept-button) (contact-height message-area)) 2)))))
	  ))))



(defmethod resize :after ((self confirm-sheet) width height border-width)
  (declare (ignore width height border-width))
  (change-layout self))

(defmethod manage-geometry ((self confirm-sheet) (child contact)
			    x y width height border-width &key) 
  (let (success-p)
    (multiple-value-bind (p-w p-h p-b-w)
	(preferred-size self)
      (if  (or 
	     (/= p-w  (contact-width self))
	     (/= p-h  (contact-height self))
	     (and width  (/= width  (contact-width child)))
	     (and height (/= height (contact-height child)))
	     )
	   (setf success-p  #'(lambda (self)
			       (progn (change-geometry self
						       :width p-w
						       :height p-h
						       :border-width p-b-w
						       :accept-p t)
				      (change-layout self))))
	   (setf success-p t)))
    (values success-p
	    (or x (contact-x child))
	    (or y (contact-y child))
	    (or width (contact-width child))
	    (or height (contact-height child))
	    (or border-width (contact-border-width child)))))
      


(defmethod preferred-size ((self confirm-sheet) &key width height border-width)
  (declare (ignore width height border-width))
  (with-slots (children parent) self
    (let* ((accumulated-width 0)
	   (highest 0)
	   (apply-button  (find-accept-button parent))
	   (cancel-button (find-cancel-button parent))
	   (message-area  (find-message-area  parent))
	   (screen (contact-screen self))
	   (pixel (getf *dialog-point-spacing* (contact-scale self)))
	   (hspace (point-pixels screen pixel :horizontal))
	   (vspace (point-pixels screen pixel :vertical)))

      ;;Find out how much space the buttons will need.
      ;;Remember: buttons are in a row, so we're interested in combined width
      ;;          and the maximum height
      (multiple-value-bind (pwidth1 pheight1 pbw1)
	  (preferred-size apply-button)
	(setf accumulated-width (+ pwidth1 pbw1 pbw1)
	      highest (+ pheight1 pbw1 pbw1))
	(when (eq (confirm-accept-only (contact-parent self)) :off)
	  (multiple-value-bind (pwidth2 pheight2 pbw2)
	      (preferred-size cancel-button)
	    (setf accumulated-width (+ accumulated-width hspace pwidth2 pbw2 pbw2)
		  highest (max highest (+ pheight2 pbw2 pbw2))))))
      
      ;;We can ignore the preferred border-width because confirm-sheet
      ;;geometry management forces a zero-width border.
      (multiple-value-bind (pwidth pheight)
	  ;; Use width/height 0 to request minimum text extent size.
	  (preferred-size message-area :width 0 :height 0) 
	(values (+ (max pwidth accumulated-width) hspace hspace 6)
		(+ pheight highest vspace vspace vspace 6)
		0))))) 


;;;----------------------------------------------------------------------------+
;;;                                                                            +
;;;   WITH-CONFIRM     Using cached confirms                                   +
;;;                                                                            +                        
;;;----------------------------------------------------------------------------+


(defmacro top-level-confirms (top)
  "A list of confirm contacts associated with TOP."
  `(getf (window-plist ,top) :confirm-cache))


(defun confirm-p (&rest initargs &key near &allow-other-keys)
  "Bind a confirm to the given initargs either by allocating one from
   the confirm cache if one exists or instantiate one"
  (assert near () "A :near initarg was not provided for CONFIRM-P")
  (let* ((near-scale (contact-scale near))	 
	 (top-level  (contact-top-level near))
	 (background (getf initargs :background))
	 (confirm    (pop (top-level-confirms top-level)))
	 (display    (contact-display near)))

    (setf background
	  (if background
	      (convert near background '(or (member :none :parent-relative) pixel pixmap))
	      (contact-current-background-pixel top-level)))

    (if confirm
	(let ((foreground      (getf initargs :foreground))	    
	      (accept-label    (getf initargs :accept-label))
	      (cancel-label    (getf initargs :cancel-label))
	      (accept-only     (getf initargs :accept-only))
	      (message         (getf initargs :message))
	      (near            (getf initargs :near))
	      (default-control (getf initargs :default-control)))
	  
	  (setf (contact-background confirm) background)
	  
	  (setf (contact-foreground confirm)
		(convert near
			 (or foreground :black)
			 '(or (member :none :parent-relative) pixel pixmap)))	  
	  (setf (confirm-accept-label confirm)
		(if accept-label
		    (convert near accept-label 'string)
		    "OK"))
	  (setf (confirm-accept-only confirm)
		(if accept-only
		    (convert near accept-only '(member :on :off))
		    :off))
	  (setf (confirm-cancel-label confirm)
		(if cancel-label
		    (convert near cancel-label 'string)
		    "Cancel"))
	  (setf (confirm-message confirm)
		(if message
		    (convert near message 'string)
		    "Press a button to continue."))
	  (setf (confirm-near confirm) near)
	  (setf (dialog-default-control confirm)
		(if default-control
		    (convert near default-control '(member :accept :cancel))
		    :accept))
	  (setf (contact-scale confirm)
		(nth (1+ (position near-scale *scales*)) *scales*)))

	(setf confirm
	      (apply
		#'make-confirm
		:parent top-level
		:background background
		:scale near-scale
		:callbacks `((:accept (,#'(lambda () (throw :exit-confirm t))))
			     (:cancel (,#'(lambda () (throw :exit-confirm nil)))))
		initargs))) 

    
    (setf (contact-state confirm) :mapped)
    (unwind-protect
	(catch :exit-confirm
	  (loop (process-next-event display)))
      (push confirm (top-level-confirms top-level)))))


(defmethod present-dialog ((confirm confirm) &key x y button state)
   (declare (type (or card16 null) x y)
	    (type (or (member :button-1 :button-2 :button-3 :button-4 :button-5) null) button)
	    (type (or mask16 null) state))
   (declare (ignore button state x y))
   (setf (contact-state confirm) :mapped))

