############################################################################
#
#	File:     slider.icn
#
#	Subject:  Procedures for slider sensors
#
#	Author:   Gregg M. Townsend
#
#	Date:     August 14, 1996
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#	These procedures implement slider using the "evmux" event
#	multiplexor instead of the usual vidget library.
#
#	slider(win, proc, arg, x, y, w, h, lb, iv, ub)  creates a slider.
#
#	slidervalue(h, v)			modifies a slider's value.
#
############################################################################
#
#  slider(win, proc, arg, x, y, w, h, lb, iv, ub)
#
#	establishes a slider and returns a handle for use with slidervalue().
#
#	x,y,w,h give the dimensions of the slider.  The slider runs vertically
#	or horizontally depending on which of w and h is larger.  20 makes a
#	nice width (or height).
#
#	lb and ub give the range of real values represented by the slider;
#	lb is the left or bottom end.  iv is the initial value.
#	proc(win, arg, value) is called as the slider is dragged to different
#	positions.
#
#  slidervalue(h, v)
#
#	changes the position of the slider h to reflect value v.
#	The underlying action procedure is not called.
#
############################################################################
#
#  Example:  A simple color picker
#
#	record color(red, green, blue)
#	global win, spot
#
#		...
#	    Fg(win, spot := NewColor(win))
#	    Color(win, spot, "gray50")
#	    FillArc(win, 10, 10, 100, 100)
#	    Fg(win, "black")
#	    h1 := slider(win, setcolor, 1, 110, 10, 20, 100, 0, 32767, 65535)
#	    h2 := slider(win, setcolor, 2, 140, 10, 20, 100, 0, 32767, 65535)
#	    h3 := slider(win, setcolor, 3, 170, 10, 20, 100, 0, 32767, 65535)
#		...
#
#	procedure setcolor(win, n, v)
#	    static fg
#	    initial fg := color(32767, 32767, 32767)
#	    fg[n] := v
#	    Color(win, spot, fg.red || "," || fg.green || "," || fg.blue)
#	end
#
#	Draw a filled circle in a mutable color that is initially gray.
#	Draw three parallel, vertical sliders of size 20 x 100.  Their values
#	run from 0 to 65535 and they are each initialized at the midpoint.
#	(The values are only used internally; the sliders are unlabeled.)
#
#	When one of the sliders is moved, call setcolor(win, n, v).
#	n, from the "arg" value when it was built, identifies the slider.
#	v is the new value of the slider.   Setcolor uses the resulting
#	color triple to set the color of the mutable color "spot".
#
#	Additional calls
#	    every slidervalue(h1 | h2 | h3, 32767)
#	    every setcolor(win, 1 to 3, 32767)
#	would reset the original gray color.  Note that explicit calls to
# 	setcolor are needed because slidervalue does not call it.
#
############################################################################
#
#  Links: evmux, graphics
#
############################################################################
#
#  Requires:  Version 9 graphics
#
############################################################################
#
#  See also:  evmux.icn
#
############################################################################

link evmux
link graphics

$define MARGIN 10

record Slider_Rec(win, proc, arg, x, y, w, h, lb, ub, n)

procedure slider(win, proc, arg, x, y, w, h, lb, iv, ub)
   local r

   r := Slider_Rec(win, proc, arg, x, y, w, h, lb, ub)
   slidervalue(r, iv)
   if h > w then 	# vertical slider
      sensor(win, &lpress, Exec_Vert_Slider, r, x, y - MARGIN, w, h + 2*MARGIN)
   else			# horizontal slider
      sensor(win, &lpress, Exec_Horiz_Slider, r, x - MARGIN, y, w + 2*MARGIN, h)
   return r
end

procedure slidervalue(r, v)
   local n

   Erase_Slider_Bar(r)			# erase old handle
   if r.lb ~= r.ub then
      v := real(v - r.lb) / (r.ub - r.lb)
   else
      v := 0.0
   v <:= 0.0
   v >:= 1.0
   if r.h > r.w then			# if vertical
      n := r.y + integer((1.0 - v) * (r.h - 1) + 0.5)
   else
      n := r.x + integer(v * (r.w - 1) + 0.5)
   Set_Slider_Posn(r, n)		# redraw track and handle
   return
end

procedure Set_Slider_Posn(r, n)
   local c

   r.n := n
   if r.h > r.w then {
      c := r.x + r.w / 2
      BevelRectangle(r.win, c - 2, r.y, 4, r.h, -2)	# vertical track
      BevelRectangle(r.win, r.x, r.n - 3, r.w, 6)	# horizontal bar
      FillRectangle(r.win, r.x + 2, r.n - 1, r.w - 4, 2)
      }
   else {
      c := r.y + r.h / 2
      BevelRectangle(r.win, r.x, c - 2, r.w, 4, -2)	# horizontal track
      BevelRectangle(r.win, r.n - 3, r.y, 6, r.h)	# vertical bar
      FillRectangle(r.win, r.n - 1, r.y + 2, 2, r.h - 4)
      }
   return
end

procedure Erase_Slider_Bar(r)
   if r.h > r.w then
      EraseArea(r.win, r.x, \r.n - 3, r.w, 6)	# horizontal bar on vert. track
   else
      EraseArea(r.win, \r.n - 3, r.y, 6, r.h)	# vertical bar on horiz. track
   return
end

procedure Exec_Vert_Slider(win, r, x, y)
   local e, h, u, args, a, v

   e := &lpress
   repeat {
      if type(e) == "integer" then {	# if a mouse event
         y <:= r.y
         y >:= r.y + r.h - 1
         if y ~= r.n then {
            Erase_Slider_Bar(r)
            Set_Slider_Posn(r, y)
            flush(r.win)
            v := real(r.y + r.h - y - 1) / real(r.h - 1)	# 0.0 to 1.0
            v := v * (r.ub - r.lb) + r.lb			# user range
            r.proc(win, r.arg, v)
            }
         if e = &lrelease then
            return
         }
      e := Event(win)
      y := &y
      }
   return
end

procedure Exec_Horiz_Slider(win, r, x, y)
   local e, h, u, args, a, v

   e := &lpress
   repeat {
      if type(e) == "integer" then {	# if a mouse event
         x <:= r.x
         x >:= r.x + r.w - 1
         if x ~= r.n then {
            Erase_Slider_Bar(r)
            Set_Slider_Posn(r, x)
            flush(r.win)
            v := real(x - r.x) / real(r.w - 1)		# 0.0 to 1.0
            v := v * (r.ub - r.lb) + r.lb		# user range
            r.proc(win, r.arg, v)
            }
         if e = &lrelease then
            return
         }
      e := Event(win)
      x := &x
      }
   return
end
