; $Id: cw_led.pro,v 1.5 2005/06/15 14:33:07 rdimeo Exp $
; +
; NAME:
;  CW_LED
;
; PURPOSE:
;  Compound widget that provides an LED-type light display
;  as a status indicator.
;
; CALLING SEQUENCE:
;  id = CW_LED(PARENT,  LABEL = LABEL,          $
;                       UNAME = uname,          $
;                       VALUE = value,          $
;                       UVALUE = uvalue,        $
;                       BG_COLOR = bg_color,    $
;                       RED = RED,              $
;                       GREEN = GREEN,          $
;                       BLUE = BLUE,            $
;                       YELLOW = YELLOW,        $
;                       ROW = row,              $
;                       XOFFSET = xoffset,      $
;                       YOFFSET = yoffset,      $
;                       OBJ_REF = obj_ref       )
;
;  Note that the value of the widget, i.e. the current light setting,
;  can be obtained using WIDGET_CONTROL:
;     WIDGET_CONTROL,id,get_value = led_setting
;  The resulting variable named led_setting here would be a byte
;  value of 1 or 0 depending on the setting.
;
;  The value of the widget can be modified using WIDGET_CONTROL
;  as follows:
;     WIDGET_CONTROL,id,set_value = new_led_setting
;  where new_led_setting is a byte value like led_setting.
;
; PARAMETERS: (Required)
;  PARENT:     widget ID of the parent widget
;
; KEYWORDS: (Optional)
;  BG_COLOR:   RGB triplet specifying the color of the background of
;              the LED.  The default is the color of the widget background.
;  XOFFSET:    The horizontal offset of the widget in units in pixels
;              relative to its parent.
;  YOFFSET:    The vertical offset of the widget in units in pixels
;              relative to its parent.
;  UVALUE:     A user value to assign to the LED CW.  This value
;              can be of any type.
;  LABEL:      Label (string) for the LED.
;  UNAME:      Set this keyword to a string that can be used to identify
;              the widget in your code. You can associate a name with
;              each widget in a specific hierarchy, and then use that
;              name to query the widget hierarchy and get the correct
;              widget ID
;  VALUE:      Byte value that indicates if a particular LED is "on"
;              value = 1B or "off", value = 0B
;  RED:        Set this keyword to create a red LED.
;  BLUE:       Set this keyword to create a blue LED.
;  GREEN:      Set this keyword to create a green LED.
;  YELLOW:     Set this keyword to create a yellow LED.
;  PURPLE:     Set this keyword to create a purple LED.
;  ROW:        If set then LEDs will be arranged in a single row
;  OBJ_REF:    Reference to the compound widget object to provide more
;              flexibility to the programmer than a conventional
;              compound widget.
;
; COMMON BLOCKS:
;  None
;
; REQUIRED PROGRAMS:
;  None
;
; REQUIREMENTS:
;  IDL 6.0 and higher (due to the use of ARRAY_INDICES)
;
; EXAMPLE:
;  An example implementation is contained at the end of this source code
;  listing and is titled CW_LED_EXAMPLE.  It illustrates how you can incorporate
;  this compound widget into an application and toggle the LEDs
;  on and off.
;
; AUTHOR:
;  Robert M. Dimeo, Ph.D.
;  NIST Center for Neutron Research
;  100 Bureau Drive
;  Gaithersburg, MD 20899
;  Phone: (301) 975-8135
;  E-mail: robert.dimeo@nist.gov
;  http://www.ncnr.nist.gov/staff/dimeo
;
; MODIFICATION HISTORY:
;  Written by RMD 05/24/05
;  Improved appearance of the LED by adding a nicer border that
;     gives more of a 3D look -- RMD 06/07/05
;
; LICENSE:
;  The software in this file is written by an employee of
;  National Institute of Standards and Technology
;  as part of the DAVE software project.
;
;  The DAVE software package is not subject to copyright
;  protection and is in the public domain. It should be
;  considered as an experimental neutron scattering data
;  reduction, visualization, and analysis system. As such,
;  the authors assume no responsibility whatsoever for its
;  use, and make no guarantees, expressed or implied,
;  about its quality, reliability, or any other
;  characteristic. The use of certain trade names or commercial
;  products does not imply any endorsement of a particular
;  product, nor does it imply that the named product is
;  necessarily the best product for the stated purpose.
;  We would appreciate acknowledgment if the DAVE software
;  is used or if the code in this file is included in another
;  product.
; -
; ************************************************ ;
pro cw_led::cleanup
ptr_free,self.on_ptr,self.off_ptr,self.storage
wdelete,self.winpix
end
; ************************************************ ;
function cw_led::on
device,get_decomposed = dc
device,decomposed = 1
wset,self.winpix
tv,*self.on_ptr,/true
wset,self.winvis
device,copy = [0,0,self.led_size,self.led_size,0,0,self.winpix]
device,decomposed = dc
return,1B
end
; ************************************************ ;
function cw_led::off
device,get_decomposed = dc
device,decomposed = 1
wset,self.winpix
tv,*self.off_ptr,/true
wset,self.winvis
device,copy = [0,0,self.led_size,self.led_size,0,0,self.winpix]
device,decomposed = dc
return,1B
end
; ************************************************ ;
function cw_led::toggle_state
; Toggle the state...
if self.state eq 0B then self.state = 1B else self.state = 0B
; ...and then turn the light off or on, depending on the state
if self.state then ret = self->on() else ret = self->off()
return,1B
end
; ************************************************ ;
pro cw_led::set_property,  value = value,          $
                           label = label,          $
                           winpix = winpix,        $
                           winvis = winvis

if n_elements(value) ne 0 then begin
   self.state = value
   if self.state then ret = self->on() else ret = self->off()
endif
if n_elements(label) ne 0 then begin
   self.label_txt = label
   widget_control,self.label_id,set_value = self.label_txt
endif
if n_elements(winpix) ne 0 then self.winpix = winpix
if n_elements(winvis) ne 0 then self.winvis = winvis
end
; ************************************************ ;
pro cw_led::get_property,  tlb = tlb,              $
                           winpix = winpix,        $
                           winvis = winvis,        $
                           win_id = win_id,        $
                           label = label,          $
                           value = value,          $
                           led_size = led_size

win_id = self.win_id
led_size = led_size
value = self.state
label = self.label_txt
tlb = self.tlb
winpix = self.winpix
winvis = self.winvis
end
; ************************************************ ;
function cw_led_get_value,id
stash = widget_info(id,/child)
widget_control,stash,get_uvalue = the_object
the_object->get_property,value = value
return,value
end
; ************************************************ ;
pro cw_led_set_value,id,val
stash = widget_info(id,/child)
widget_control,stash,get_uvalue = the_object
the_object->set_property,value = val
; ...and then turn the light off or on, depending on the state
if byte(val) then ret = the_object->on() else ret = the_object->off()
end
; ************************************************ ;
function cw_led_event,event
return,event
end
; ************************************************ ;
pro cw_led_notify_realize,id
stash = widget_info(id,/parent)
;stash = widget_info(widget_info(id,/parent),/child)
widget_control,stash,get_uvalue = the_object
the_object->get_property,led_size = led_size,      $
   win_id = win_id,value = state
widget_control,win_id,get_value = winvis
window,/free,/pixmap,xsize = led_size,ysize = led_size
winpix = !d.window
the_object->set_property,winpix = winpix,winvis = winvis
; Set the initial state of the button
if state then ret = the_object->on() else ret = the_object->off()
; Set the cursor to the standard arrow (on Windows) so that
; the user doesn't know that he/she's using a draw widget here
; (where it would turn into a cross-hairs).
device,/cursor_original
end
; ************************************************ ;
pro cw_led_kill_notify,id
widget_control,id,get_uvalue = the_object
obj_destroy,the_object
end
; ************************************************ ;
function cw_led::build_widgets
tlb = widget_base(self.parent,uvalue = *self.storage,          $
      uname = self.uname,event_func = 'CW_LED_EVENT',          $
      pro_set_value = 'cw_led_set_value',                      $
      xoffset = self.xoffset,yoffset = self.yoffset,           $
      func_get_value = 'cw_led_get_value',/base_align_center   )

if ~self.row then begin
   base = widget_base(tlb,/col,uvalue = self,                  $
         kill_notify = 'CW_LED_KILL_NOTIFY',xpad = 0,ypad = 0  )
endif else begin
   base = widget_base(tlb,/row,uvalue = self,                  $
         kill_notify = 'CW_LED_KILL_NOTIFY',xpad = 0,ypad = 0  )
endelse
self.tlb = tlb
if self.label_txt ne '' then begin
   self.label_id = widget_label(base,value = self.label_txt,      $
      /dynamic_resize                                             )
endif else begin
   self.label_id = 0L
endelse
self.win_id = widget_draw(base,xsize = self.led_size,          $
   ysize = self.led_size,                                      $
   notify_realize = 'CW_LED_NOTIFY_REALIZE',                   $
   /align_center                                               )
return,1B
end
; ************************************************ ;
function cw_led::init_leds
base = widget_base()
sys_colors = widget_info(base,/system_colors)
color = self.bg_color

;color = sys_colors.face_3d
light_3d = sys_colors.light_3d
shadow_3d = sys_colors.shadow_3d
light_edge_3d = sys_colors.light_edge_3d
dark_shadow_3d = sys_colors.dark_shadow_3d
widget_control,base,/destroy
; Use true color
device,decomposed = 1
xsize = (ysize = self.led_size)
b1 = bytarr(3,xsize,ysize)
for i = 0,2 do b1[i,*,*] = color[i]
xc = (xsize-1)/2.  & yc = (ysize-1)/2.
r = 0.7*xc
xm = rebin(indgen(xsize),xsize,ysize,/sample)
ym = rebin(transpose(indgen(ysize)),xsize,ysize,/sample)
diff = (xm-xc)^2+(ym-yc)^2 - r^2
filled = where(diff le 0.0,count,complement = unfilled)
xspot = 1.0*r & yspot = 1.8*r
rspot = 0.2*r
white = ((xm-xspot)^2+(ym-yspot)^2)-rspot^2
where_white = where(white le 0.0,count)
white_ind = array_indices(bytarr(xsize,ysize),where_white)

angle = !radeg * atan(ym-yc,xm-xc)
border = (((xm-xc)^2+(ym-yc)^2) le (1.0*xc)^2) and    $
         (((xm-xc)^2+(ym-yc)^2) ge r^2)
r1 = 0.85*xc

border1_cond = (((xm-xc)^2+(ym-yc)^2) le xc^2) and    $
               (((xm-xc)^2+(ym-yc)^2) ge r1^2)
border1 = where(border1_cond)
border2_cond = (((xm-xc)^2+(ym-yc)^2) le r1^2) and    $
               (((xm-xc)^2+(ym-yc)^2) ge r^2)
border2 = where(border2_cond)

border1_ind = array_indices(bytarr(xsize,ysize),border1)
border2_ind = array_indices(bytarr(xsize,ysize),border2)
sb1 = size(border1_ind) & sb2 = size(border2_ind)

; Now create the color change around the border
xx = xm-xc & yy = ym-yc & h = sqrt(xx^2+yy^2)
theta = asin(yy/h)
nth = n_elements(theta)
this_angle = -45.0

border1_color = bytscl(sin(1.*theta))
border2_color = 255B-border1_color


border1_image = border1_color+bytarr(xsize,ysize)
border1_image = rot(border1_image,this_angle,/cubic)
border2_image = border2_color+bytarr(xsize,ysize)
border2_image = rot(border2_image,this_angle,/cubic)

;angle_cond = (angle le 45.0) and (angle ge -135.0)
angle_cond = (angle le 10.0) and (angle ge -120.0)
border_hi = angle_cond and border
border_hi_indices = where(border_hi)

border_lo = ~angle_cond and border
border_lo_indices = where(border_lo)

border_lo_ind = array_indices(bytarr(xsize,ysize),border_lo_indices)
border_hi_ind = array_indices(bytarr(xsize,ysize),border_hi_indices)
sb_hi = size(border_hi_ind)
sb_lo = size(border_lo_ind)

; Create a blurred "half-moon" from which we'll extract the border
; and put "behind" the LED.  This will provide a visual indication
; of depth.
;
; R = radius of the LED
; XC = "half-size" of the entire LED image array
;
r1 = 0.9*xc

rcspot = 0.15*r
close_white = (xm-xspot)^2+(ym-yspot)^2-rcspot^2
where_close_white = where(close_white le 0.0,count)
close_white_indices = array_indices(bytarr(xsize,ysize),where_close_white)

b2 = b1
b3 = b1
findices = array_indices(bytarr(xsize,ysize),filled)
sf = size(findices)
sw = size(white_ind)
case self.color of
'red':   $
   begin
      index1 = 0 & onval1 = 240B & offval1 = 70B
      index2 = 1 & onval2 = 0B & offval2 = 0B
      index3 = 2 & onval3 = 0B & offval3 = 0B
   end
'green': $
   begin
      index1 = 1 & onval1 = 240B & offval1 = 70B
      index2 = 0 & onval2 = 0B & offval2 = 0B
      index3 = 2 & onval3 = 0B & offval3 = 0B
   end
'blue':  $
   begin
      index1 = 2 & onval1 = 255B & offval1 = 130B
      index2 = 0 & onval2 = 0B & offval2 = 0B
      index3 = 1 & onval3 = 0B & offval3 = 0B
   end
'yellow':   $
   begin
      index1 = 0 & onval1 = 248B & offval1 = 184B
      index2 = 1 & onval2 = 221B & offval2 = 134B
      index3 = 2 & onval3 = 0B & offval3 = 11B
   end
'purple':   $
   begin
      index1 = 0 & onval1 = 160B & offval1 = 80B
      index2 = 1 & onval2 = 32B & offval2 = 0B
      index3 = 2 & onval3 = 240B & offval3 = 80B
   end
endcase

for j = 0,sf[2]-1 do begin
   b2[index1,findices[0,j],findices[1,j]] = onval1
   b2[index2,findices[0,j],findices[1,j]] = onval2
   b2[index3,findices[0,j],findices[1,j]] = onval3

   b3[index1,findices[0,j],findices[1,j]] = offval1
   b3[index2,findices[0,j],findices[1,j]] = offval2
   b3[index3,findices[0,j],findices[1,j]] = offval3
endfor
scw = size(close_white_indices)

; Add the white spot
for j = 0,sw[2]-1 do $
   for i = 0,2 do $
      b2[i,white_ind[0,j],white_ind[1,j]] = 255B

for j = 0,scw[2]-1 do $
   for i = 0,2 do $
      b3[i,close_white_indices[0,j],close_white_indices[1,j]] = 255B

; Now blur the LED images
width = 5
for i = 0,2 do begin
   b2[i,*,*] = smooth(b2[i,*,*],/edge_truncate,width)
   b3[i,*,*] = smooth(b3[i,*,*],/edge_truncate,width)
endfor



; Add the high border to the lights
for j = 0,sb1[2]-1 do begin
   for i = 0,2 do begin
      b2[i,border1_ind[0,j],border1_ind[1,j]] = border1_image[border1_ind[0,j],border1_ind[1,j]]
      b3[i,border1_ind[0,j],border1_ind[1,j]] = border1_image[border1_ind[0,j],border1_ind[1,j]]
   endfor
endfor
for j = 0,sb2[2]-1 do begin
   for i = 0,2 do begin
      b2[i,border2_ind[0,j],border2_ind[1,j]] = border2_image[border2_ind[0,j],border2_ind[1,j]]
      b3[i,border2_ind[0,j],border2_ind[1,j]] = border2_image[border2_ind[0,j],border2_ind[1,j]]
   endfor
endfor

; Add the widget colored border
unfilled = (((xm-xc)^2+(ym-yc)^2) ge xc^2)
where_unfilled = where(unfilled)
unfill_ind = array_indices(bytarr(xsize,ysize),where_unfilled)
suf = size(unfill_ind)

ufindices = array_indices(bytarr(xsize,ysize),unfill_ind)
su = size(ufindices)
for j = 0,su[2]-1 do begin
   for i = 0,2 do begin
      b2[i,ufindices[0,j],ufindices[1,j]] = color[i]
      b3[i,ufindices[0,j],ufindices[1,j]] = color[i]
   endfor
endfor

*self.on_ptr = b2
*self.off_ptr = b3

return,1B
end
; ************************************************ ;
function cw_led::init,  parent,                    $
                        red = red,                 $
                        green = green,             $
                        blue = blue,               $
                        yellow = yellow,           $
                        purple = purple,           $
                        row = row,                 $
                        uname = uname,             $
                        value = value,             $
                        uvalue = uvalue,           $
                        led_size = led_size,       $
                        bg_color = bg_color,       $
                        xoffset = xoffset,         $
                        yoffset = yoffset,         $
                        label = label

if n_params() eq 0 then return,0B
if n_elements(bg_color) eq 0 then begin
   base = widget_base()
   bg_color = (widget_info(base,/system_colors)).face_3d
   widget_control,base,/destroy
endif
self.bg_color = bg_color
if n_elements(xoffset) eq 0 then xoffset = 0L
if n_elements(yoffset) eq 0 then yoffset = 0L
self.xoffset = xoffset & self.yoffset = yoffset
if n_elements(led_size) eq 0 then led_size = 25
self.led_size = led_size
if n_elements(value) eq 0 then value = 1B
self.state = value
self.parent = parent
if n_elements(label) eq 0 then label = ''
self.label_txt = label
self.row = keyword_set(row)   ; default is for a column layout
if keyword_set(red) then color = 'red'
if keyword_set(green) then color = 'green'
if keyword_set(blue) then color = 'blue'
if keyword_set(yellow) then color = 'yellow'
if keyword_set(purple) then color = 'purple'
if ~keyword_set(red) and      $
   ~keyword_set(green) and    $
   ~keyword_set(blue) and     $
   ~keyword_set(purple) and   $
   ~keyword_set(yellow)       $
   then color = 'green'
self.color = color
if n_elements(uname) eq 0 then uname = ''
self.uname = uname
if n_elements(uvalue) eq 0 then uvalue = ''
self.storage = ptr_new(uvalue)
self.on_ptr = ptr_new(/allocate_heap)
self.off_ptr = ptr_new(/allocate_heap)
ret = self->init_leds()
return,1B
end
; ************************************************ ;
pro cw_led__define
void =   {  cw_led,                                $
            parent:0L,                             $
            bg_color:bytarr(3),                    $
            uname:'',                              $
            storage:ptr_new(),                     $
            led_size:0L,                           $
            xoffset:0L,                            $
            yoffset:0L,                            $
            label_id:0L,                           $
            win_id:0L,                             $
            winpix:0L,                             $
            winvis:0L,                             $
            tlb:0L,                                $
            color:'',                              $
            label_txt:'',                          $
            row:0B,                                $
            state:0B,                              $
            on_ptr:ptr_new(),                      $
            off_ptr:ptr_new()                      }

end
; ************************************************ ;
function cw_led,  parent,                          $
                  _Extra = extra,                  $
                  obj_ref = obj_ref
obj_ref = obj_new('cw_led',parent,_Extra = extra)
ret = obj_ref->build_widgets()
obj_ref->get_property,tlb = tlb
return,tlb
end
; ************************************************ ;
; ************************************************ ;
pro cw_led_example_event,event
case widget_info(event.id,/uname) of
'QUIT':     widget_control,event.top,/destroy
'TOGGLE':   $
   begin
      for i = 0,4 do begin
         uname = 'MY_LED'+strtrim(string(i+1),2)
         led_id = widget_info(event.top,find_by_uname = uname)
         widget_control,led_id,get_value = old_value
         if old_value eq 1 then new_value = 0 else new_value = 1
         widget_control,led_id,set_value = new_value
         if new_value eq 1 then button_label = 'Lights off' else $
            button_label = 'Lights on'
         widget_control,event.id,set_value = button_label
      endfor
   end
else:
endcase
end
; ************************************************ ;
pro cw_led_example
title = 'Example usage of CW_LED'
tlb = widget_base(/col,/tlb_frame_attr,/base_align_center,title = title)
row = widget_base(tlb,/row)
ls = 80
void = cw_led(row,value = 0,label = 'Label 1',/blue,uname = 'MY_LED1', $
   LED_SIZE = ls)
void = cw_led(row,value = 0,label = 'Label 2',/red,uname = 'MY_LED2', $
   LED_SIZE = ls)
void = cw_led(row,value = 0,label = 'Label 3',/green,uname = 'MY_LED3', $
   LED_SIZE = ls)
void = cw_led(row,value = 0,label = 'Label 4',/yellow,uname = 'MY_LED4', $
   LED_SIZE = ls)
void = cw_led(row,value = 0,label = 'Label 5',/purple,uname = 'MY_LED5', $
   LED_SIZE = ls)
void = widget_button(tlb,value = 'Lights on',uname = "TOGGLE",/dynamic_resize)
void = widget_button(tlb,value = 'Quit',uname = "QUIT")
widget_control,tlb,/realize
xmanager,'cw_led_example',tlb,/no_block
end