private/variable-graphics.ss
#lang scheme/gui
;;; PLT Scheme Simulation Collection
;;; variable.ss
;;; Copyright (c) 2008 M. Douglas Williams
;;;
;;; This library is free software; you can redistribute it and/or
;;; modify it under the terms of the GNU Lesser General Public
;;; License as published by the Free Software Foundation; either
;;; version 2.1 of the License, or (at your option) any later version.
;;;
;;; This library is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;;; Lesser General Public License for more details.
;;;
;;; You should have received a copy of the GNU Lesser General Public
;;; License along with this library; if not, write to the Free
;;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
;;; 02111-1307 USA.
;;;
;;; Version  Date      Description
;;; 1.0.0    11/28/08  Initial release.  (Doug Williams)

(require "variable.ss")

(provide (all-defined-out))

;;; -----------------------------------------------------------------------------
;;;                               Variable Slider
;;; -----------------------------------------------------------------------------
;;; A variable slider is a slider that can automatically send its value to a
;;; simulation collection variable.  The class variable-slider% is a subclass of
;;; the MrEd slider% class and only works with integers in the range of -10000 to
;;; 10000.  The min and max values are set when the slider is created.

;;; variable-slider%
(define variable-slider%
  (class slider%
    ;; --------------------------------------------------------------------------
    ;;                            Initializations
    ;; --------------------------------------------------------------------------
    (init label)
    (init min-value)
    (init max-value)
    (init parent)
    (init ((variable-init variable) #f))
    (unless (or (variable? variable-init)
                (not variable-init))
      (error
       "initialization of variable-slider%: expected either a (simulation collection) variable or #f, given ~a"
       variable-init))
    ;; --------------------------------------------------------------------------
    ;;                          Inherited Methods
    ;; --------------------------------------------------------------------------
    (inherit get-value)
    ;; --------------------------------------------------------------------------
    ;;                          Private Variables
    ;; --------------------------------------------------------------------------
    (define variable #f)
    ;; --------------------------------------------------------------------------
    ;;                             Set Variable
    ;; --------------------------------------------------------------------------
    (define/public (set-variable v)
      (unless (or (not v)
                  (variable? v))
        (error 'set-variable
               "expect argument of either #f or type variable?, given ~a"
               v))
      (set! variable v)
      (when variable
        (set-variable-value! variable (get-value))))
    ;; --------------------------------------------------------------------------
    ;;                            Initialize Object
    ;; --------------------------------------------------------------------------
    (super-instantiate
        (label min-value max-value parent)
      ;; Callback will set the variable value
      (callback
       (lambda (slider event)
         (when variable
           (set-variable-value! variable (get-value))))))
    (set-variable variable-init)))

;;; -----------------------------------------------------------------------------
;;;                               Variable Gauge
;;; -----------------------------------------------------------------------------
;;; A variable gauge is a gauge that is automatically updated when the its assoc-
;;; iated variable, if any, is updated.
(define variable-gauge%
  (class gauge%
    ;; --------------------------------------------------------------------------
    ;;                            Initializations
    ;; --------------------------------------------------------------------------
    (init label)
    (init((range-init range)))
    (unless (or (and (integer? range-init)
                     (<= 1 range-init 10000))
                (and (variable? range-init)
                     (<= 1 (variable-value range-init) 10000)))
      (error
       "initialization of variable-gauge%: expected one of integer in the range 1 to 10000 or  a (simulation collection) variable whose value is an integer in the range 1 to 10000, given ~a"
       range-init))
    (init parent)
    (init ((variable-init variable) #f))
    (unless (or (variable? variable-init)
                (not variable-init))
      (error
       "initialization of variable-gauge%: expected either a (simulation collection) variable or #f, given ~a"
       variable-init))
    ;; --------------------------------------------------------------------------
    ;;                          Inherited Methods
    ;; --------------------------------------------------------------------------
    (inherit set-value)
    ;; --------------------------------------------------------------------------
    ;;                          Private Variables
    ;; --------------------------------------------------------------------------
    (define range
      (cond ((integer? range-init)
             range-init)
            ((variable? range-init)
             (variable-value range-init))))
    (define variable #f)
    (define range-monitor #f)
    (define value-monitor #f)
    ;; --------------------------------------------------------------------------
    ;;                              Set Range
    ;; --------------------------------------------------------------------------
    (define/override (set-range r)
      (unless (or (and (integer? r)
                       (<= 1 r 10000))
                  (and (variable? r)
                       (<= 1 (variable-value r) 10000)))
        (error 'set-variable
               "expected argument of one an integer in range 1 to 10000 or a (simulation collection) variable whose value is an integer in range 1 to 10000, given ~a"
               r))
      ;; Remove any existing range monitor
      (when range-monitor
        (variable-remove-set-monitor! range range-monitor)
        (set! range-monitor #f))
      ;; Set the local range variable
      (set! range r)
      ;; Set the parent range and set up the range variable monitor
      (cond ((integer? range)
             (super set-range range))
            ((variable? range)
             (super set-range (variable-value range))
             (set! range-monitor
                   (variable-add-set-monitor!
                    range 'after
                    (lambda (variable value)
                      (super set-range value)))))))
    ;; --------------------------------------------------------------------------
    ;;                             Set Variable
    ;; --------------------------------------------------------------------------
    (define/public (set-variable v)
      (unless (or (not v)
                  (variable? v))
        (error 'set-variable
               "expect argument of either #f or type variable?, given ~a"
               v))
      ;; Remove any existing value monitor
      (when value-monitor
        (variable-remove-set-monitor! variable value-monitor)
        (set! value-monitor #f))
      ;; Set the local variable variable
      (set! variable v)
      ;; Set the parent value and set up the value variable monitor
      (if variable
          (begin
            (if (variable-initialized? variable)
                (set-value (variable-value variable))
                (set-value 0))
            (set! value-monitor
                  (variable-add-set-monitor!
                   variable 'after
                   (lambda (variable value)
                     (if (variable-initialized? variable)
                         (set-value value)
                         (set-value 0))))))
          (set-value 0)))
    ;; --------------------------------------------------------------------------
    ;;                            Initialize Object
    ;; --------------------------------------------------------------------------
    (super-instantiate
        (label range parent))
    (set-range range-init)
    (set-variable variable-init)))

;;; -----------------------------------------------------------------------------
;;;                              Variable Message
;;; -----------------------------------------------------------------------------
(define variable-message%
  (class message%
    ;; --------------------------------------------------------------------------
    ;;                            Initializations
    ;; --------------------------------------------------------------------------
    (init label)
    (init parent)
    (init ((variable-init variable) #f))
    ;; --------------------------------------------------------------------------
    ;;                          Inherited Methods
    ;; --------------------------------------------------------------------------
    (inherit set-label)
    ;; --------------------------------------------------------------------------
    ;;                          Private Variables
    ;; --------------------------------------------------------------------------
    (define variable #f)
    (define value-monitor #f)
    ;; --------------------------------------------------------------------------
    ;;                             Set Variable
    ;; --------------------------------------------------------------------------
    (define/public (set-variable v)
      (when value-monitor
        (variable-remove-set-monitor! variable value-monitor)
        (set! value-monitor #f))
      (set! variable v)
      (when variable
        (set! value-monitor
              (variable-add-set-monitor!
               variable 'after
               (lambda (variable value)
                 (set-label (number->string value)))))))
    ;; --------------------------------------------------------------------------
    ;;                            Initialize Object
    ;; --------------------------------------------------------------------------
    (super-instantiate
        ((if (and (variable? variable-init)
                  (variable-initialized? variable-init))
             (number->string (variable-value variable-init))
             "")
         parent))
    (set-variable variable-init)))

(define variable-text-field%
  (class text-field%
    (init label)
    (init parent)
    (init ((variable-init variable) #f))
    (inherit set-value)
    (define variable #f)
    (define value-monitor #f)
    (define/public (set-variable v)
      (when value-monitor
        (variable-remove-set-monitor! variable value-monitor)
        (set! value-monitor #f))
      (set! variable v)
      (when variable
        (set! value-monitor
              (variable-add-set-monitor!
               variable 'after
               (lambda (variable value)
                 (set-value (number->string value)))))))
    (super-instantiate
        (label parent))
    (set-variable variable-init)))

(define variable-monitor%
  (class horizontal-panel%
    (init label)
    (init parent)
    (init ((variable-init variable) #f))
    (super-instantiate
     (parent))
    (define label-message
      (instantiate message%
        (label this)))
    (define value-message
      (instantiate message%
        ((if (and (variable? variable-init)
                  (variable-initialized? variable-init))
             (number->string (variable-value variable-init))
             "")
         this)
        (stretchable-width #t)))
    (define variable #f)
    (define value-monitor #f)
    (define/public (set-variable v)
      (when value-monitor
        (variable-remove-set-monitor! variable value-monitor)
        (set! value-monitor #f))
      (set! variable v)
      (when variable
        (set! value-monitor
              (variable-add-set-monitor!
               variable 'after
               (lambda (variable value)
                 (send value-message
                       set-label (number->string value)))))))
    (set-variable variable-init)))