############################################################################
#
#	File:     vradio.icn
#
#	Subject:  Procedures for radio buttons
#
#	Authors:  Jon Lipp and Gregg M. Townsend
#
#	Date:     August 14, 1996
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#  Vidgets defined in this file:
#	Vradio_entry
#	Vradio_frame
#
#  Utility procedures in this file:
#	Vradio_buttons()
#	Vvert_radio_buttons()
#	Vhoriz_radio_buttons()
#	init_format_Vrb()
#	format_Vradio_frame()	
#
############################################################################

link vstyle

############################################################################
# Vradio - the radio button.
############################################################################

record Vradio_entry_rec (win, s, callback, id, style, aw, ah, don, ax, ay, uid, P, D, V)

#
# Creation procedure.
#
procedure Vradio_entry(params[])
   local self
   static procs

   initial procs := Vstd(event_Vradio_entry, draw_Vradio_entry,
      outline_radio_pane, resize_Vidget, inrange_Vpane, init_Vradio_entry,
                        couplerset_Vradio_entry)
   self := Vradio_entry_rec ! params
   self.uid := Vget_uid()
   Vset_style(self, self.style)
   self.V := procs
   self.P := Vstd_pos()
   self.V.init(self)
   return self
end

procedure init_Vradio_entry (self)
   local p

   if /self.callback then 
      _Vbomb("must pass a coupler variable to a Vradio_entry button")
   self.D.init(self)
end


#
#  Draw the frame around the radio buttons.
#
procedure outline_radio_pane(self)
   GrooveRectangle(self.win, self.ax, self.ay, self.aw, self.ah)
end


#
#  Draw the radio button.  If coupler's value is this id, draw "on".
#
procedure draw_Vradio_entry(self)
   if self.callback.value === self.id then {
      self.D.draw_on(self)
      self.don := 1
      }
   else {
      self.D.draw_off(self)
      self.don := &null
      }
end

#
#  The coupler notified us, turn "off".
#
procedure couplerset_Vradio_entry(self)
   self.D.draw_off(self)
   self.don := &null
end

#
#  If first time in this button, set coupler, draw "on".
#  If mouse releases on me, return my own record structure.
#
procedure event_Vradio_entry(self, e)

   if self.callback.value ~=== self.id | /self.don then {
      self.callback.V.set(self.callback, self, self.id)
      self.D.draw_on(self)
      self.don := 1
      }
   if \e === (&lrelease|&mrelease|&rrelease) then 
      return self
end


############################################################################
# Vradio_frame
############################################################################

record Vradio_frame_rec(win, cv, callback, id, aw, ah, data,
   lookup, draw, ax, ay, uid, P, V)

#
# Creation procedure.
#
procedure Vradio_frame(params[])
   local self, p
   static procs

   initial {
      procs := Vstd(event_Vradio_frame, draw_Vframe, outline_radio_pane,
                   resize_Vframe, inrange_Vpane, init_Vframe,
                   couplerset_Vpane, insert_Vframe, null_proc,
                   lookup_Vframe, set_abs_Vframe, set_value_Vradio_frame)
   }

   self := Vradio_frame_rec ! params
   self.uid := Vget_uid()
   self.V := procs
   self.P := Vstd_pos()
   self.V.init(self)
   p := \self.callback
   self.callback := Vcoupler()
   add_clients_Vinit(self.callback, p, self)
   
   return self
end

#
#  Distribute mouse event to proper radio button.  If returns
#  a value, (mouse released) notify callbacks, return text label
#  of radio button selected.
#
procedure event_Vradio_frame(self, e, x, y)
   local focus, rv

   if \self.callback.locked then fail
   if e ~=== &lpress & e ~=== &mpress & e ~=== &rpress then fail
   focus := self.V.lookup(self, x, y)
   (\focus).V.event(focus, e)
   repeat {
      e := Event(self.win)
      if e === "\^s" then
         until Event(self.win) === (&lpress|&mpress|&rpress) ;
      if self.V.inrange(self, &x, &y) then 
         focus := self.V.lookup(self, &x, &y)
      if rv := (\focus).V.event(focus, e) then {
         self.data := rv.s
         self.callback.V.set(self.callback, rv, rv.s)
         return rv.s
      }
   }
end

#
#  Set the radio frame according to string label passed in.  Match with
#  string label of a button.  Null sets to no button.
#
procedure set_value_Vradio_frame(self, value)
   local old, kid, id, s, k

   if (/value | *value = 0 | value === V_DUMMY_ID) then {
      kid := &null
      id := V_DUMMY_ID
      s := ""
      }
   else {
      kid := self.cv.curr_id
      id := self.cv.value
      s := self.data
      every (k := !self.lookup | fail) do 
         if value === k.s then {
            id := k.id
            kid := k
            s := value
            break
            }
      }

   old := self.cv.curr_id
   self.cv.curr_id := kid
   self.cv.value := id
   self.data := s

   self.callback.V.set(self.callback, self, self.data)

   (\old).D.draw_off(old)	# clear current button
   (\kid).D.draw_on(kid)	# set new button

   return
end

############################################################################
# Vradio_buttons -
# Construct radio buttons. Parameters:
# 	w - window, proc - the callback procedure,
#	s[] - a list of button labels. 
############################################################################
procedure Vradio_buttons(params[])
   return Vvert_radio_buttons ! params
end


#
# params: (w, s, callback, id, style)
#
procedure Vvert_radio_buttons(params[])
   local frame, x, y, ins, win, s, callback, id, style
   local rb_frame, max, cv, i, rb, first, uncentered
   static type

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

   if ins := Vinsert_check(params) then {
      frame := pop(params); x := pop(params); y:= pop(params)
      }
   win := params[1]
   s := params[2]
   callback := params[3]
   id := params[4]
   style := params[5]
   uncentered := params[6]

   Vwin_check(win, "Vradio_buttons()")
   if type(s) ~== "list" then 
      _Vbomb("data parameter to Vradio_buttons must be a list of strings") 
   cv := Vmenu_coupler()
   rb_frame := Vradio_frame(win, cv, callback, id)
   if /uncentered then {
      max := 0
      every i := !s do max <:= TextWidth(win, i)
      max +:= 8
      }
   if \style == (V_CIRCLE | V_CHECK | V_DIAMOND | 
	 V_CHECK_NO | V_CIRCLE_NO | V_DIAMOND_NO) then
      max +:= 4 + WAttrib(win, "fheight")
   every i := 1 to *s do {
      rb := Vradio_entry(win, s[i], cv, i, style, max)
      VInsert(rb_frame, rb, 0, (i-1)*rb.ah)
   }

   init_format_Vrb(rb_frame)
   format_Vradio_frame(rb_frame)

   if \ins then VInsert(frame, rb_frame, x, y)
   return rb_frame
end

procedure Vhoriz_radio_buttons(params[])
   local frame, x, y, ins, win, s, callback, id, style, hpos
   local rb_frame, max, cv, i, rb, first
   static type

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

   if ins := Vinsert_check(params) then {
      frame := pop(params); x := pop(params); y:= pop(params)
      }
   win := params[1]
   s := params[2]
   callback := params[3]
   id := params[4]
   style := params[5]

   Vwin_check(win, "Vradio_buttons()")
   if type(s) ~== "list" then
      _Vbomb("data parameter to Vradio_buttons must be a list of strings")
   cv := Vmenu_coupler()
   rb_frame := Vradio_frame(win, cv, callback, id)
   hpos := 0
   every i := 1 to *s do {
      rb := Vradio_entry(win, s[i], cv, i, style)
      VInsert(rb_frame, rb, hpos, 0)
      hpos +:= rb.aw
   }

   init_format_Vrb(rb_frame)
   rb_frame.V.resize(rb_frame, 0, 0, Vmin_frame_width(rb_frame), 
      Vmin_frame_height(rb_frame))

   if \ins then VInsert(frame, rb_frame, x, y)
   return rb_frame
end

#
#  Set to no radio button selected, format size of frame.
#	
procedure init_format_Vrb(rb_frame)

   rb_frame.cv.value :=  V_DUMMY_ID
   rb_frame.cv.curr_id := &null
   rb_frame.data := ""
end

#
#  Get size of frame based on entries.
#
procedure format_Vradio_frame(self, width)
local maxwidth, child

   maxwidth := \width | Vmin_frame_width(self) + 4
   every child := !self.lookup do {
      child.P.w := maxwidth
   }
   self.V.resize(self, 0, 0, maxwidth, Vmin_frame_height(self))
end