############################################################################
#
#	File:     vslider.icn
#
#	Subject:  Procedures for sliders
#
#	Authors:  Jon Lipp and Gregg M. Townsend
#
#	Date:     April 1, 1997
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#  Vidgets defined in this file:
#	Vvslider
#	Vhslider
#
#  Utility procedures in this file:
#	Vvert_slider()
#	Vhoriz_slider()
#
############################################################################
#
#  Includes: vdefns.icn
#
############################################################################
#
#  Links:  vidgets
#
############################################################################

link vidgets

$include "vdefns.icn"

record Vslider_rec (win, callback, id, aw, ah, discont,
   ax, ay, data, pad, ws, cv_range, rev, pos, uid, drawn, P, V)

############################################################################
# Vvslider
############################################################################

procedure procs_Vvslider()
   static procs
   initial procs := Vstd(event_Vvslider, draw_Vvslider, outline_Vslider,
                        resize_Vvslider, inrange_Vpane, init_Vvslider,
                        couplerset_Vvslider,,,,,set_value_Vvslider)
   return procs
end

procedure Vvslider(params[])
   local self

   self := Vslider_rec ! params[1:7|0]
   Vwin_check(self.win, "Vvert_slider()")
   if (\self.aw, not numeric(self.aw) ) then
      _Vbomb("invalid width parameter to Vvert_slider()")
   if (\self.ah, not numeric(self.ah) ) then
      _Vbomb("invalid length parameter to Vvert_slider()")
   
   self.uid := Vget_uid()
   self.V := procs_Vvslider()
   self.P := Vstd_pos()

   self.V.init(self)
   return self
end

procedure draw_Vvslider(s)
local val

   s.drawn := 1
   s.V.outline(s)
   val := (s.callback.value - s.callback.min) * s.ws / s.cv_range
   if \s.rev then
      val := s.ws - val + s.pad
   else
      val +:= s.pad
   s.pos := val
   draw_Vvslider_bar(s)
end

procedure event_Vvslider(s, e)
local value

   if \s.callback.locked then fail
   if e === (&lpress|&mpress|&rpress) then
      until e === (&lrelease|&mrelease|&rrelease) do {
         value := ((&y - s.ay - s.pad) / s.ws) * s.cv_range
         if \s.rev then
            s.callback.V.set(s.callback, s, s.callback.max - value, s.discont)
         else
            s.callback.V.set(s.callback, s, s.callback.min + value, s.discont)
         s.data := s.callback.value
         update_Vvslider(s, 1)
         e := Event(s.win)
         }
   else
      fail				# not our event
   if \s.discont then
      s.callback.V.set(s.callback, s, s.callback.value)
   update_Vvslider(s)
   return s.callback.value
end

procedure update_Vvslider(s, active)
local val

   val := (s.callback.value - s.callback.min) * s.ws / s.cv_range
   if \s.rev then
      val := s.ws - val + s.pad
   else
      val +:= s.pad
   s.pos := val
   draw_Vvslider_bar(s, active)
   return s.callback.value
end

procedure draw_Vvslider_bar(s, active)
local ww, d

   ww := s.aw - 4
   EraseArea(s.win, s.ax + 2, s.ay + 2, ww, s.ah - 4)
   if \active then {
      d := -1
      FillRectangle(s.win, s.ax + 4, s.ay + s.pos - ww + 2, ww - 4, 2 * ww - 4)
      }
   else
      d := 1
   BevelRectangle(s.win, s.ax + 2, s.ay + s.pos - ww, ww, 2 * ww, d)
   BevelRectangle(s.win, s.ax + 3, s.ay + s.pos, ww - 2, 1 - ww, d)
   BevelRectangle(s.win, s.ax + 3, s.ay + s.pos, ww - 2, ww - 1, d)
end

procedure set_value_Vvslider(s, value)
   couplerset_Vvslider(s, , value)
   return
end

procedure couplerset_Vvslider(s, caller, value)

   value := numeric(value) | s.callback.min
   if s.callback.value === value then fail
   s.callback.V.set(s.callback, caller, value)
   s.data := s.callback.value
   if \s.drawn then
      update_Vvslider(s)
end

procedure init_Vvslider(s)
   static type

   initial type := proc("type", 0)	# protect attractive name

   /s.aw := VSlider_DefWidth
   /s.ah := VSlider_DefLength
   s.aw <:= VSlider_MinWidth
   s.ah <:= VSlider_MinAspect * s.aw
   if /s.callback | type(s.callback) == "procedure" then
      _Vbomb("Vvslider requires a coupler variable callback")
   s.pad := s.aw - 2
   s.ws := real(s.ah - 2 * s.pad)
   s.cv_range := s.callback.max - s.callback.min
   init_Vpane(s)
end

procedure resize_Vvslider(s, x, y, w, h)

   resize_Vidget(s, x, y, w, h)
   if s.aw > s.ah then {
      s.V := procs_Vhslider()
      return s.V.resize(s, x, y, w, h)
      }
   s.pad := s.aw - 2
   s.ws := real(s.ah - 2 * s.pad)
   s.cv_range := s.callback.max - s.callback.min
end


############################################################################
# Vhslider 
############################################################################

procedure procs_Vhslider()
   static procs

   initial procs := Vstd(event_Vhslider, draw_Vhslider, outline_Vslider,
                        resize_Vhslider, inrange_Vpane, init_Vhslider,
                        couplerset_Vhslider,,,,,set_value_Vhslider)
   return procs
end

procedure Vhslider(params[])
   local self

   self := Vslider_rec ! params[1:7|0]
   self.aw :=: self.ah
   Vwin_check(self.win, "Vhoriz_slider()")
   if (\self.ah, not numeric(self.ah) ) then
      _Vbomb("invalid width parameter to Vhoriz_slider()")
   if (\self.aw, not numeric(self.aw) ) then
      _Vbomb("invalid length parameter to Vhoriz_slider()")

   self.uid := Vget_uid()
   self.V := procs_Vhslider()
   self.P := Vstd_pos()

   self.V.init(self)
   return self
end

procedure draw_Vhslider(s)
local val

   s.drawn := 1
   s.V.outline(s)
   val := (s.callback.value - s.callback.min) * s.ws / s.cv_range
   if \s.rev then
      val := s.ws - val + s.pad
   else
      val +:= s.pad
   s.pos := val
   draw_Vhslider_bar(s)
end

procedure event_Vhslider(s, e)
local value

   if \s.callback.locked then fail
   if e === (&lpress|&mpress|&rpress) then
      until e === (&lrelease|&mrelease|&rrelease) do {
         value := ((&x - s.ax - s.pad) / s.ws) * s.cv_range
         if \s.rev then
            s.callback.V.set(s.callback, s, s.callback.max - value, s.discont)
         else
            s.callback.V.set(s.callback, s, s.callback.min + value, s.discont)
         s.data := s.callback.value
         update_Vhslider(s, 1)
         e := Event(s.win)
         }
   else
      fail				# not our event
   if \s.discont then
      s.callback.V.set(s.callback, s, s.callback.value)
   update_Vhslider(s)
   return s.callback.value
end

procedure update_Vhslider(s, active)
local val

   val := (s.callback.value - s.callback.min) * s.ws / s.cv_range
   if \s.rev then
      val := s.ws - val + s.pad
   else
      val +:= s.pad
   s.pos := val
   draw_Vhslider_bar(s, active)
   return s.callback.value
end

procedure draw_Vhslider_bar(s, active)
local hh, d

   hh := s.ah - 4
   EraseArea(s.win, s.ax + 2, s.ay + 2, s.aw - 4, hh)
   if \active then {
      d := -1
      FillRectangle(s.win, s.ax + s.pos - hh + 2, s.ay + 4, 2 * hh - 4, hh - 4)
      }
   else
      d := 1
   BevelRectangle(s.win, s.ax + s.pos - hh, s.ay + 2, 2 * hh, hh, d)
   BevelRectangle(s.win, s.ax + s.pos, s.ay + 3, 1 - hh, hh - 2, d)
   BevelRectangle(s.win, s.ax + s.pos, s.ay + 3, hh - 1, hh - 2, d)
end

procedure set_value_Vhslider(s, value)
   couplerset_Vhslider(s, , value)
   return
end

procedure couplerset_Vhslider(s, caller, value)

## break a cycle in callbacks by checking value.
   value := numeric(value) | s.callback.min
   if s.callback.value === value then fail
   s.callback.V.set(s.callback, caller, value)
   s.data := s.callback.value
   if \s.drawn then
      update_Vhslider(s)
end

procedure init_Vhslider(s)
   static type

   initial type := proc("type", 0)	# protect attractive name

   /s.ah := VSlider_DefWidth
   /s.aw := VSlider_DefLength
   s.ah <:= VSlider_MinWidth
   s.aw <:= VSlider_MinAspect * s.ah
   if /s.callback | type(s.callback) == "procedure" then
      _Vbomb("Vhslider requires a coupler variable callback")
   s.pad := s.ah - 2
   s.ws := real(s.aw - 2 * s.pad)
   s.cv_range := s.callback.max - s.callback.min
   init_Vpane(s)
end

procedure resize_Vhslider(s, x, y, w, h)

   resize_Vidget(s, x, y, w, h)
   if s.aw < s.ah then {
      s.V := procs_Vvslider()
      return s.V.resize(s, x, y, w, h)
      }
   s.pad := s.ah - 2
   s.ws := real(s.aw - 2 * s.pad)
   s.cv_range := s.callback.max - s.callback.min
end

############################################################################
# Utilities - slider wrapper procedures.
############################################################################

procedure outline_Vslider(s)
   BevelRectangle(s.win, s.ax, s.ay, s.aw, s.ah, -2)	# draw trough
end

procedure Vmake_slider(slider_type, w, callback, id, length, width,
                       min, max, init, discontinuous)
local cv, sl, cb, t
   static type

   initial type := proc("type", 0)	# protect attractive name

   /min := 0
   /max := 1.0
   if not numeric(min) | not numeric(max) | (\init, not numeric(init)) then
      _Vbomb("non-numeric min, max, or init parameter passed to Vxxxxx_slider()")
   if max < min then { min :=: max; t := 1 }
   cv := Vrange_coupler(min, max, init)
   sl := slider_type(w, cv, id, width, length, discontinuous)
   sl.rev := t
   
   add_clients_Vinit(cv, callback, sl)
   return sl
end

############################################################################
# Vvert_slider(w, callback, id, width, length, lower_bound, upper_bound,
#              initial_value) 
############################################################################
procedure Vvert_slider(params[])
local frame, x, y, ins, t, self

   if ins := Vinsert_check(params) then {
      frame := pop(params); x := pop(params); y:= pop(params)
      }

   params[6] :=: params[7]
   push(params, Vvslider)
   self :=  Vmake_slider ! params
   if \ins then VInsert(frame, self, x, y)
   return self
end

############################################################################
# Vhoriz_slider(w, callback, id, width, length, left_bound, right_bound,
#              initial_value) 
############################################################################
procedure Vhoriz_slider(params[])
local frame, x, y, ins, self

   if ins := Vinsert_check(params) then {
      frame := pop(params); x := pop(params); y:= pop(params)
      }

   push(params, Vhslider)
   self :=  Vmake_slider ! params
   if \ins then VInsert(frame, self, x, y)
   return self
end