#| Rewrite these to use snip-wrapper% automatically |#

(module button-snip mzscheme
  
  (require
   (lib "mred.ss" "mred")
   (lib "class.ss")
   (lib "etc.ss")
   "snip-wrapper.ss")
  
  (provide
   text-button-snip%
   button-snip%
   toggle-button-snip%
   embedded-button%
   embedded-text-button%
   embedded-toggle-button%)
  
  (define embedded-button%
    (class snip-wrapper%
      (init images callback)
      (super-new
       (snip (new button-snip%
                  (images images)
                  (callback callback))))))
  
  (define embedded-text-button%
    (class snip-wrapper%
      (init label callback)
      (super-new
       (snip (new text-button-snip%
                  (label label)
                  (callback callback))))))
  
  (define embedded-toggle-button%
    (class snip-wrapper%
      (init images-off images-on turn-off turn-on (state 'on))
      (field [btn (new toggle-button-snip%
                       (images-off images-off)
                       (images-on images-on)
                       (turn-off turn-off)
                       (turn-on turn-on)
                       (state state))])
      ;; Provide the original toggle-button-snip% interface
      (define/public (set-state x) (send btn set-state x))
      (super-new (snip btn))))
  
  ;; a snip of a button that can be pushed to invoke a given callback
  (define button-snip%
    (class image-snip%
      (inherit load-file)
      (init images)
      (init-field callback)
      (field
       [got-click? false]
       [inside? false]
       [image (car images)]
       [depressed (cdr images)])
      
      ;; (string? . -> . void?)
      ;; set the image to be displayed on the button when it is not clicked
      (define/public (set-images i)
        (set! image (car i))
        (set! depressed (cdr i))
        (load-file image))
      
      ;; Should I be calling super-on-event?
      (define/override (on-event dc x y editorx editory event)
        (case (send event get-event-type)
          [(left-down)
           (set! got-click? true)
           (set! inside? true)
           (load-file depressed)]
          [(left-up)
           (load-file image)
           (when (and got-click? inside?)
             (callback this event))
           (set! got-click? false)
           (set! inside? false)]
          [(enter)
           (set! inside? true)
           (when got-click?
             (load-file depressed))]
          [(leave)
           (set! inside? false)
           (when got-click?
             (load-file image))]
          [else (void)]))
      
      (super-new)
      (load-file image)))
  
  (define BUTTON-H-SPACE 5)
  (define BUTTON-V-SPACE 2)

  (define UNCLICKED-COLOR (make-object color% 220 220 175))
  (define CLICKED-COLOR (make-object color% 100 100 50))

  ;; a textual button of the same type
  (define text-button-snip%
    (class string-snip%
      (init label)
      (init-field callback)
      (field
       [got-click? false]
       [inside? false])

      (define/private (inc b v)
	(when b
	  (set-box! b (+ v (unbox b)))))

      (define/override get-extent 
	(opt-lambda (dc x y [w #f] [h #f] [db #f] [dt #f] [dl #f] [dr #f])
	  (super get-extent dc (+ x BUTTON-H-SPACE) (+ y BUTTON-V-SPACE) w h db dt dl dr)
	  (inc w (* 2 BUTTON-H-SPACE))
	  (inc h (* 2 BUTTON-V-SPACE))
	  (inc db BUTTON-V-SPACE)
	  (inc dt BUTTON-V-SPACE)
	  (inc dl BUTTON-H-SPACE)
	  (inc dr BUTTON-H-SPACE)))

      (define/override (draw dc x y t l r b dx dy caret)
	(let ([p (send dc get-pen)]
	      [b (send dc get-brush)]
	      [smoothing (send dc get-smoothing)])
	  (send dc set-pen "black" 1 'solid)
	  (send dc set-brush 
		(if got-click? CLICKED-COLOR UNCLICKED-COLOR)
		'solid)
	  (send dc set-smoothing 'aligned)
	  (let ([w (box 0)]
		[h (box 0)])
	    (get-extent dc x y w h)
	    (send dc draw-rounded-rectangle x y (unbox w) (unbox h) BUTTON-H-SPACE))
	  (send dc set-pen p)
	  (send dc set-brush b)
	  (send dc set-smoothing smoothing))
	(super draw dc (+ x BUTTON-H-SPACE) (+ y BUTTON-V-SPACE) 
	       t l r b 
	       (+ dx BUTTON-H-SPACE) (+ dy BUTTON-V-SPACE)
	       caret))

      (define/override (on-event dc x y editorx editory event)
        (case (send event get-event-type)
          [(left-down)
           (set! got-click? true)
           (set! inside? true)
	   (refresh)]
          [(left-up)
           (when (and got-click? inside?)
             (callback this event))
           (set! got-click? false)
           (set! inside? false)
	   (refresh)]
          [(enter)
           (set! inside? true)]
          [(leave)
           (set! inside? false)]
          [else (void)]))

      (inherit get-admin)
      (define/private (refresh)
	(let ([a (get-admin)])
	  (when a
	    (send a needs-update this 0 0 1000 100))))
      
      (super-make-object label)
      (inherit set-style)
      (set-style control-style)))
  
  ;; a toggle button that displays different images
  (define toggle-button-snip%
    (class button-snip%
      (inherit set-images)
      (init-field images-off images-on turn-off turn-on (state 'on))
      
      ;; Emulates clicking the button to a certain state
      (define/public (turn astate)
        (case astate
          [(off) (set-state 'off)
                 (turn-on this #f)]
          [(on) (set-state 'on)
                (turn-off this #f)]))
      
      (define/public (set-state value)
        (case value
          [(off) (set-images images-off)
                 (set! state 'off)]
          [(on) (set-images images-on)
                (set! state 'on)]))
      
      (super-new
       (images (case state
                 [(on) images-on]
                 [(off) images-off]))
       (callback
        (lambda (b e)
          ;; NOTE: I lose the event right here, but turn can't require it.
          ;; Since it's public.
          (case state
            [(on) (turn 'off)]
            [(off) (turn 'on)]))))))
  
  ;;;;;;;;;;
  ;; tests
  #|
  (require
   (lib "locked-pasteboard.ss" "mrlib" "private" "aligned-pasteboard")
   (lib "click-forwarding-editor.ss" "mrlib"))
  
  (define (test)
    (define f (new frame% (label "test") (width 200) (height 200)))
    (define e (new (locked-pasteboard-mixin
                    (click-forwarding-editor-mixin pasteboard%))))
    (define c (new editor-canvas% (editor e) (parent f)))
    (define b (new button-snip%
                   (images (cons (build-path (collection-path "icons") "turn-up.gif")
                                 (build-path (collection-path "icons") "turn-up-click.gif")))
                   (callback
                    (lambda (b e)
                      (message-box "Test" "Horray!")))))
    (send e insert b)
    (send f show #t))
  
  (define (test2)
    (define f (new frame% (label "test") (width 200) (height 200)))
    (define e (new (locked-pasteboard-mixin
                    (click-forwarding-editor-mixin pasteboard%))))
    (define c (new editor-canvas% (editor e) (parent f)))
    (define t (new text%))
    (define es (new editor-snip% (editor t)))
    (define b (new toggle-button-snip%
                   (images-on (cons (build-path (collection-path "icons") "turn-up.gif")
                                  (build-path (collection-path "icons") "turn-up-click.gif")))
                   (images-off (cons (build-path (collection-path "icons") "turn-down.gif")
                                  (build-path (collection-path "icons") "turn-down-click.gif")))
                   (turn-on
                    (lambda (b e)
                      (send* t (erase) (insert "Up"))))
                   (turn-off
                    (lambda (b e)
                      (send* t (erase) (insert "Down"))))))
    (send e insert es 50 0)
    (send e insert b)
    (send f show #t))
  |#
)
