
(module feature mzscheme
  (require (prefix mred: (lib "mred.ss" "mred"))
	   (lib "class.ss")
	   (lib "file.ss")
	   (lib "pretty.ss")
	   (lib "etc.ss")
	   (lib "list.ss")
	   "base.ss"
	   "utils.ss")

  ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;; Mixins for GUI features
  ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  (define gb:make-text-label-snip%
    (lambda (cl deflabel)
      (class cl
	(inherit get-style gb-need-recalc-size)
	(override*
	  [get-frame%
	   (lambda ()
	     (class (super get-frame%)
	       (inherit-field controls)
	       (override*
		 [get-kind (lambda () deflabel)])
	       (super-new)
	       (private-field
		 [label-buffer (make-one-line/callback-edit controls "Label:"
							    (lambda (txt)
							      (set! label txt)
							      (gb-need-recalc-size))
							    label)])))]
	  [gb-instantiate-arguments
	   (lambda ()
	     `(,@(super gb-instantiate-arguments)
	       [label ,(get-label)]))])
	(field
	  [label deflabel])
	(public*
	  [get-label
	   (lambda ()
	     label)]
	  [get-label-size
	   (lambda (dc)
	     (let-values ([(w h d a) (send dc get-text-extent label 
					   (send (get-style) get-font))])
	       (values w h)))]
	  [draw-label
	   (lambda (dc x y)
	     (send dc draw-text label x y))]
	  
	  [label-install
	   (lambda (n)
	     (set! label n))])
	(override*
	  [copy
	   (lambda ()
	     (let ([o (super copy)])
	       (send o label-install label)
	       o))]
	  [write
	   (lambda (stream)
	     (super write stream)
	     (send stream put (string->bytes/utf-8 label)))]
	  [read
	   (lambda (stream version)
	     (super read stream version)
	     (label-install ((get-bytes->string version) (send stream get-bytes))))])
	(super-new))))

  (define gb:make-callback-snip%
    (lambda (cl)
      (class cl
	(inherit-field name)
	(public*
	  [get-callback-kinds (lambda () (list "-callback"))]
	  [get-callback-code (lambda ()
			       (map (lambda (x) '(lambda (w e) (void))) (get-callback-kinds)))]
	  [get-callback-names
	   (lambda ()
	     (map
	      (lambda (ct)
		(string->symbol (string-append name ct)))
	      (get-callback-kinds)))]
	  [gb-get-unified-callback
	   (lambda ()
	     `(lambda (b e) (,(car (get-callback-names)) b e)))])
	(override*
	  [gb-instantiate-arguments
	   (lambda ()
	     `(,@(super gb-instantiate-arguments)
	       [callback ,(gb-get-unified-callback)]))]
	  [gb-aux-instantiate
	   (lambda (mode)
	     (append
	      (if (or (output-mode-as-class? mode)
		      (output-mode-no-free-vars? mode))
		  (map (lambda (n c) 
			 (if (output-mode-as-class? mode)
			     `(public* [,n ,c])
			     `(define ,n ,c)))
		       (get-callback-names) (get-callback-code))
		  null)
	      (super gb-aux-instantiate mode)))])
	(super-new))))
  
  (define gb:make-text-labelled-snip%
    (lambda (cl deflabel)
      (class (gb:make-text-label-snip% cl deflabel)
	(inherit-field w h)
	(inherit get-label-size draw-label gb-need-recalc-size)
	(private-field
	  [hmargin 2]
	  [vertical-label? (init-vertical-label?)])
	(public*
	  [get-label-top-margin (lambda () 0)]
	  [init-vertical-label? (lambda () #f)]

	  [get-min-body-size
	   (lambda (dc)
	     (values 0 0))]
	  [draw-body
	   (lambda (dc x y w h)
	     (void))]
	  
	  [labelpos-install
	   (lambda (vert?)
	     (set! vertical-label? vert?))])
	(override*
	  [get-frame%
	   (lambda ()
	     (class (super get-frame%)
	       (inherit-field controls)
	       (super-new)
	       (private-field
		 [direction-radio
		  (make-object mred:radio-box% "Label Position:" '("Top" "Left")
			       controls 
			       (lambda (r e)
				 (set! vertical-label? (zero? (send direction-radio get-selection)))
				 (gb-need-recalc-size))
			       '(horizontal))])
	       (send direction-radio set-selection (if vertical-label? 0 1))))]
	  [gb-get-min-size
	   (lambda (dc)
	     (let-values ([(x y) (get-label-size dc)]
			  [(x2 y2) (get-min-body-size dc)]
			  [(+x +y) (if vertical-label?
				       (values max +)
				       (values (lambda (a b) (+ a b hmargin)) max))])
	       (values (+x x x2) (+y (+ y (get-label-top-margin)) y2))))]
	  [draw
	   (lambda (dc x y . other)
	     (draw-label dc x (+ y (get-label-top-margin)))
	     (let*-values ([(lw lh) (get-label-size dc)]
			   [(dx dy) (if vertical-label?
					(values 0 lh)
					(values (+ lw hmargin) 0))])
	       (with-clipping-region dc (+ x dx) (+ y dy) (- w dx) (- h dy)
		 (lambda ()
		   (draw-body dc (+ x dx) (+ y dy) (- w dx) (- h dy))))))]
	  
	  [gb-get-style
	   (lambda ()
	     (cons (if vertical-label?
		       'vertical-label
		       'horizontal-label)
		   (super gb-get-style)))]

	  [copy
	   (lambda ()
	     (let ([o (super copy)])
	       (send o labelpos-install vertical-label?)
	       o))]
	  [write
	   (lambda (stream)
	     (super write stream)
	     (send stream put (if vertical-label? 1 0)))]
	  [read
	   (lambda (stream version)
	     (super read stream version)
	     (labelpos-install (positive? (send stream get-exact))))])
	(super-new))))


  
  (define gb:make-layout-snip%
    (lambda (cl)
      (class cl
	(inherit gb-need-recalc-size)
	(override*
	  [get-frame%
	   (lambda ()
	     (class (super get-frame%)
	       (inherit-field controls)
	       (super-new)
	       (private-field
		 [layout-direction-radio
		  (make-object mred:radio-box%
			       "Layout:"
			       '("Vertical" "Horizontal")
			       controls 
			       (lambda (r e)
				 (set! vertical-layout? 
				       (zero? (send layout-direction-radio get-selection)))
				 (gb-need-recalc-size))
			       '(horizontal))])
	       (send layout-direction-radio set-selection (if vertical-layout? 0 1))))])
	(field
	  [vertical-layout? (init-vertical-layout?)])
	(public*
	  [init-vertical-layout? (lambda () #t)]
	  [layout-install
	   (lambda (vert?)
	     (set! vertical-layout? vert?))])
	(override*
	 [gb-get-style
	  (lambda ()
	    (cons (if vertical-layout?
		      'vertical
		      'horizontal)
		  (super gb-get-style)))]
	 [copy
	  (lambda ()
	    (let ([o (super copy)])
	      (send o layout-install vertical-layout?)
	      o))]
	 [write
	  (lambda (stream)
	    (super write stream)
	    (send stream put (if vertical-layout? 1 0)))]
	 [read
	  (lambda (stream version)
	    (super read stream version)
	    (layout-install (positive? (send stream get-exact))))])
	(super-new))))

  ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;; Mixins for configuration options
  ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  (define gb:make-configure-snip%
    (lambda (cl tag init)
      (class cl
	(private-field
	  [v init])
	(override*
	  [get-tagged-value
	   (lambda (t)
	     (if (eq? t tag)
		 v
		 (super get-tagged-value t)))]
	  [set-tagged-value
	   (lambda (t v-in)
	     (if (eq? t tag)
		 (set! v v-in)
		 (super set-tagged-value t v-in)))]

	  [copy
	   (lambda ()
	     (let ([o (super copy)])
	       (send o set-tagged-value tag v)
	       o))])
	(super-new))))

  (define gb:make-boolean-configure-snip%
    (lambda (cl tag label init change-cb init-cb)
      (class (gb:make-configure-snip% cl tag init)
	(inherit gb-need-recalc-size get-tagged-value set-tagged-value)
	(override*
	  [get-frame%
	   (lambda ()
	     (define this-snip this)
	     (class (super get-frame%)
	       (inherit-field controls)
	       (override*
		[find-control
		 (lambda (t)
		   (if (eq? t tag)
		       c
		       (super find-control t)))])
	       (super-new)
	       (private-field
		 [c (make-object mred:check-box% 
				 label controls
				 (lambda (c e)
				   (set-tagged-value tag (send c get-value))
				   (change-cb this this-snip)
				   (gb-need-recalc-size)))])
	       (send c set-value (get-tagged-value tag))
	       (init-cb this this-snip)))]
	  [write
	   (lambda (stream)
	     (super write stream)
	     (send stream put (if (get-tagged-value tag) 1 0)))]
	  [read
	   (lambda (stream version)
	     (super read stream version)
	     (set-tagged-value tag (positive? (send stream get-exact))))])
	(super-new))))

  (define gb:make-multi-checkable-snip%
    (lambda (cl)
      (class (gb:make-boolean-configure-snip% cl 'multi "Multiple Lines" #f
						  (lambda (f snip)
						    (send snip multi-changed f))
						  void)
	 (inherit get-tagged-value)
	 (public*
	  [get-multi
	   (lambda () (get-tagged-value 'multi))]
	  [multi-changed
	   (lambda (f)
	     (send (send f find-control 'hscroll) enable (get-multi)))])
	 (super-new))))
  
  (define gb:make-select-configure-snip%
    (lambda (cl tag label choices)
      (class (gb:make-configure-snip% cl tag 0)
	(inherit gb-need-recalc-size get-tagged-value set-tagged-value)
	(override*
	  [get-frame%
	   (lambda ()
	     (class (super get-frame%)
	       (inherit-field controls)
	       (super-new)
	       (private-field
		 [c (make-object mred:choice% 
				 label choices controls
				 (lambda (c e)
				   (set-tagged-value tag (send c get-selection))
				   (gb-need-recalc-size)))])
	       (send c set-selection (get-tagged-value tag))))]
	  [write
	   (lambda (stream)
	     (super write stream)
	     (send stream put (get-tagged-value tag)))]
	  [read
	   (lambda (stream version)
	     (super read stream version)
	     (set-tagged-value tag (send stream get-exact)))])
	(super-new))))
  
  (provide gb:make-text-label-snip%
	   gb:make-callback-snip%
	   gb:make-text-labelled-snip%
	   gb:make-layout-snip%

	   gb:make-configure-snip%
	   gb:make-boolean-configure-snip%
	   gb:make-multi-checkable-snip%
	   gb:make-select-configure-snip%))
