;+
; NAME:
;   RMD_WIDGET_DRAW
;
; PURPOSE:
;   RMD_WIDGET_DRAW is a compound widget that allows the user to
;   zoom into data and select various groups to plot using a slider
;   control.
;
; CATEGORY:
;   Compound widgets.
;
; CALLING SEQUENCE:
;       Widget = RMD_WIDGET_DRAW(Parent)
;
;   To get or set the value of a RMD_WIDGET_DRAW, use the GET_VALUE and
;   SET_VALUE keywords to WIDGET_CONTROL. The value of a RMD_WIDGET_DRAW
;   is a pointer to an array of pointers to structures with the following
;	fields below:
;
;   DATASTR = {QTY:QTY,ERR:ERR,X:X,Y:Y,XLABEL:XLABEL,YLABEL:YLABEL,TITLE:TITLE}
;
;   Here QTY is an N by M array where N is the number of "channels" and M is
;   the number of "groups."  The independent variable X is a vector of channel
;   values of length N and Y is a vector of group values of length M.
;	XLABEL, YLABEL, and TITLE are used to specify labels for the plot.
;
; INPUTS:
;       Parent:     The ID of the parent widget.
;
; KEYWORD PARAMETERS:
;
;   VALUE:  The value of the widget. This value is a pointer to a
;			structure that contains the data elements as indicated above.
;
;           WIDGET_CONTROL, widget, set_value=value
;
;   UVALUE:     The user value to be associated with the widget.
;   UNAME:      The user name to be associated with the widget.
;   XSIZE:      The width of the base.
;   YSIZE:      The height of the base.
;	SAVE_DATA:	If set then a drop-down menu is displayed allowing user
;				to save the currently displayed data or all of the data
;				into an ascii file.
;	TRACK_CURSOR	If set, then the cursor coordinates are displayed
;					in a text box.
;
; OUTPUTS:
;       The ID of the created widget is returned.
;
; SIDE EFFECTS:
;   There is no event generated from this widget.  All events are
;   internal to the compound widget.
;
; MODIFICATION HISTORY:
;   4 February 2002, R.M.Dimeo
;   5 February 2002, R.M.Dimeo	- 	Made more intelligent choices in selecting
;									the identity of the widget for storing the
;									user value....a base (PLOTBASE) that is the
;									child of the top level base (BASE).
;								-	Added SAVE_DATA keyword and functionality as
;									described above
;								-	Array of pointers can be passed in as the value
;									of the widget.
;	6 February 2002, R.M.Dimeo	-	Added TRACK_CURSOR keyword and functionality as
;									described above.
;-
;###############################################################################
; 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 of if the code in this file is
;  included in another product.
;
;###############################################################################
;;;;;;;;;;;;;
pro rmd_widget_draw_cleanup,base
; Cleans up the pointer as the plot window is being destroyed
; Here BASE is the plotting window (butWin)
compile_opt hidden
tlb = widget_info(base,/parent)
widget_control,tlb,get_uvalue = pState,/no_copy
wdelete,(*pState).winPix
zPtrs = *(*pState).value
nPtrs = n_elements(zPtrs)
for j = 0,nPtrs-1 do begin
  ptr_free,zPtrs[j]
endfor
ptr_free,(*pState).datPtr
ptr_free,(*pState).value
ptr_free,pState
return
end
;;;;;;;;;;;;;
pro rmd_widget_draw_plotData,event
compile_opt hidden
; event.id 	= BUTWIN
; event.top	= BASE
child = widget_info(event.top,/child)
widget_control,child,get_uvalue = pState,/no_copy
if n_elements((*(*(*pState).value)[0]).qty) le 1 then begin
  widget_control,child,set_uvalue = pState,/no_copy
  return
endif
widget_control,(*pState).slider,get_value = index
index = index[0] - 1
if index eq -1 then begin
  widget_control,child,set_uvalue = pState,/no_copy
  return
endif
qty = (*(*(*pState).value)[0]).qty[*,index]
err = (*(*(*pState).value)[0]).err[*,index]
x = (*(*(*pState).value)[0]).x
y = (*(*(*pState).value)[0]).y[index]
xlabel = (*(*(*pState).value)[0]).xlabel
ylabel = (*(*(*pState).value)[0]).ylabel
title = (*(*(*pState).value)[0]).title

if (*pState).autoscale eq 1 then begin
  xmin = 1.e25 & xmax = -1.e25
  ymin = 1.e25 & ymax = -1.e25
  for j = 0,(*pState).nPointers-1 do begin
    xmin = xmin < min((*(*(*pState).value)[j]).x)
    xmax = xmax > max((*(*(*pState).value)[j]).x)
    ymin = ymin < min((*(*(*pState).value)[j]).qty[*,index])
    ymax = ymax > max((*(*(*pState).value)[j]).qty[*,index])
  endfor
  dx = xmax-xmin
  (*pState).xviewrange = [xmin-0.1*dx,xmax+0.1*dx]
  dy = ymax-ymin
  (*pState).yviewrange = [ymin-0.1*dy,ymax+0.1*dy]
endif

title = title + ': '+strcompress(string(y))
plot,x,qty,psym = 4,xrange = (*pState).xviewrange,$
     yrange = (*pState).yviewrange,xstyle = 1,ystyle = 1,$
     xtitle = xlabel,$
     ytitle = ylabel,$
     title = title
errplot,x,qty-err,qty+err,width = 0.0

if (*pState).nPointers gt 1 then begin
  for j = 1,(*pState).nPointers-1 do begin

	qty = (*(*(*pState).value)[j]).qty[*,index]
	err = (*(*(*pState).value)[j]).err[*,index]
	x = (*(*(*pState).value)[j]).x
	y = (*(*(*pState).value)[j]).y[index]

	oplot,x,qty,psym = 5
	errplot,x,qty-err,qty+err,width = 0.0
  endfor
endif
widget_control,child,set_uvalue = pState,/no_copy
return
end
;;;;;;;;;;;;;
function rmd_widget_draw_get_val,id
compile_opt hidden
; id = BASE
plotBase = widget_info(id,/child)
widget_control,plotBase,get_uvalue = pState,/no_copy
ret = (*pState).value
widget_control,plotBase,set_uvalue = pState,/no_copy
return,ret
end
;;;;;;;;;;;;;
pro rmd_widget_draw_set_val,id,value
compile_opt hidden
; id = BASE
plotBase = widget_info(id,/child)
widget_control,plotBase,get_uvalue = pState,/no_copy

zPtrs = *value
(*pState).nPointers = n_elements(zPtrs)
; Here zPtrs is an array of pointers

; Are the relevant fields in the structure?
for j = 0,(*pState).nPointers-1 do begin
  names = tag_names((*(*value)[0]))
  qtyOk = where(names eq "QTY")
  errOk = where(names eq "ERR")
  xOk = where(names eq "X")
  yOk = where(names eq "Y")
  xlabelOk = where(names eq "XLABEL")
  ylabelOk = where(names eq "YLABEL")
  titleOk = where(names eq "TITLE")
  if qtyOk[0] eq -1 then (*(*value)[j]).qty = 0.0
  if errOk[0] eq -1 then (*(*value)[j]).err = 0.0*(*(*value)[j]).qty
  s = size((*(*value)[j]).qty)
  if xOk[0] eq -1 then (*(*value)[j]).x = findgen(s[1])
  if yOk[0] eq -1 then (*(*value)[j]).y = 0.0
  if xlabelOk[0] eq -1 then (*(*value)[j]).xlabel = ""
  if ylabelOk[0] eq -1 then (*(*value)[j]).ylabel = ""
  if titleOk[0] eq -1 then (*(*value)[j]).title = ""
endfor

; Do a little bookkeeping and freeing of pointers
; in the pointer array to prevent memory leakage.
n = n_elements(*(*pState).value)
if n gt 1 then begin
  thisPtr = (*(*pState).value)[0:n-1]
  ptr_free,thisPtr[0:n-1]
endif

(*(*pState).value) = [(*pState).datPtr]
(*(*pState).value)[0] = (*value)[0]

for j = 1,(*pState).nPointers-1 do begin
  (*(*pState).value) = [*(*pState).value,(*value)[j]]
endfor
;print,(*(*pState).value)
dims = size((*(*value)[0]).qty,/n_dimensions)
s = size((*(*value)[0]).qty)

if s[0] eq 1 then ngroups = 1
if s[0] eq 2 then ngroups = s[2]
(*pState).ngroups = ngroups

if ngroups eq 1 then begin
  widget_control,(*pState).slider,set_slider_max = 1,set_slider_min = 0
endif else begin
  widget_control,(*pState).slider,set_slider_max = ngroups,set_slider_min = 1
endelse
butWin = (*pState).butWin
wset,(*pState).winPix
widget_control,plotBase,set_uvalue = pState,/no_copy
rmd_widget_draw_plotData,{event,id:butWin,top:id,handler:0l}
widget_control,plotBase,get_uvalue = pState,/no_copy
wset,(*pState).winVis
device,copy = [0,0,!d.x_size,!d.y_size,0,0,(*pState).winPix]
empty
widget_control,plotBase,set_uvalue = pState,/no_copy
return
end
;;;;;;;;;;;;;
pro rmd_widget_draw_realize,base
compile_opt hidden
;Gets the necessary values when the widget is realized.
; BASE = BUTWIN
tlb = widget_info(base,/parent)
widget_control,tlb,get_uvalue = pState,/no_copy
widget_control,(*pState).butWin,get_value = winVis
(*pState).winVis = winVis
widget_control,tlb,set_uvalue = pState,/no_copy
return
end
;;;;;;;;;;;;
pro rmd_widget_drawDraw,event
compile_opt hidden
; All of the zooming stuff goes here
; event.id = BUTWIN
plotBase = widget_info(event.id,/parent)
tlb = widget_info(plotBase,/parent)
widget_control,plotBase,get_uvalue = pState,/no_copy
if n_elements((*(*(*pState).value)[0]).qty) le 2 then begin
  widget_control,plotBase,set_uvalue = pState,/no_copy
  return
endif
case event.type of
0:	begin		; button press
	  (*pState).mouse = event.press
	  if (*pState).mouse eq 4 then begin
	    (*pState).autoscale = 1
	  endif
	  if (*pState).mouse eq 1 then begin
	    (*pState).xbox[0] = event.x
	    (*pState).ybox[0] = event.y
	    wset,(*pState).winVis
	    device,copy = [0,0,!d.x_size,!d.y_size,0,0,(*pState).winPix]
	    empty
	    (*pState).autoscale = 0
	    widget_control,event.id,draw_motion_events = 1
	  endif
	end
1:	begin	; button release
	  xll = (*pState).xbox[0] < (*pState).xbox[1]
	  yll = (*pState).ybox[0] < (*pState).ybox[1]
	  w = abs((*pState).xbox[1] - (*pState).xbox[0])
	  h = abs((*pState).ybox[1] - (*pState).ybox[0])
	  xur = xll + w
	  yur = yll + h
	  ll = convert_coord(xll,yll,/device,/to_data)
	  ur = convert_coord(xur,yur,/device,/to_data)
	  (*pState).xviewrange = [ll[0],ur[0]]
	  (*pState).yviewrange = [ll[1],ur[1]]
	  wset,(*pState).winPix
	  widget_control,plotBase,set_uvalue = pState,/no_copy
	  rmd_widget_draw_plotData,{event,id:event.id,top:tlb,handler:0l}
	  widget_control,plotBase,get_uvalue = pState,/no_copy
	  wset,(*pState).winVis
	  device,copy = [0,0,!d.x_size,!d.y_size,0,0,(*pState).winPix]
	  if (*pState).track_cursor eq 0 then begin
	    widget_control,event.id,draw_motion_events = 0
	  endif
	  (*pState).mouse = 0B
	end
2:	begin	; mouse motion
	  if (*pState).mouse eq 1 then begin
	  	(*pState).xbox[1] = event.x
	  	(*pState).ybox[1] = event.y
	  	xc = [(*pState).xbox[0],event.x,event.x,(*pState).xbox[0],(*pState).xbox[0]]
	  	yc = [(*pState).ybox[0],(*pState).ybox[0],event.y,event.y,(*pState).ybox[0]]
	  	wset,(*pState).winVis
	  	device,copy = [0,0,!d.x_size,!d.y_size,0,0,(*pState).winPix]
	  	plots,xc,yc,/device
	  	empty
	  endif else begin
	    fcode = '(e15.2)'
	    coords = convert_coord(event.x,event.y,/device,/to_data)
	    outText = '(x,y)=('+strcompress(string(coords[0],format = fcode))+$
	              ','+strcompress(string(coords[1],format = fcode))+')'
	    widget_control,(*pState).text,set_value = outText
	  endelse
	end
else:
endcase
widget_control,plotBase,set_uvalue = pState,/no_copy
return
end
;;;;;;;;;;;;;
pro rmd_widget_draw_changeGroup,ev
compile_opt hidden
; ev.id = SLIDER
slider = ev.id
rowBase = widget_info(ev.id,/parent)
plotBase = widget_info(rowBase,/parent)
tlb = widget_info(plotBase,/parent)
widget_control,plotBase,get_uvalue = pState,/no_copy
butWin = (*pState).butWin
wset,(*pState).winPix
widget_control,plotBase,set_uvalue = pState,/no_copy
rmd_widget_draw_plotData,{event,id:butWin,top:tlb,handler:0l}
widget_control,plotBase,get_uvalue = pState,/no_copy
wset,(*pState).winVis
device,copy = [0,0,!d.x_size,!d.y_size,0,0,(*pState).winPix]
empty
widget_control,plotBase,set_uvalue = pState,/no_copy
return
end
;;;;;;;;;;;;;
function rmd_widget_draw_event,event
compile_opt hidden
; event.id = DROPMENU
; event.top = calling widget's TLB
; event.handler = ROWBASE
rowBase = event.handler
plotBase = widget_info(rowBase,/parent)
widget_control,plotBase,get_uvalue = pState,/no_copy
dropVal = event.value

case dropVal of
1:	begin	; save all in memory
	end
2:	begin	; save current display

	if n_elements((*(*(*pState).value)[0]).qty) le 1 then begin
  		widget_control,plotBase,set_uvalue = pState,/no_copy
  		return,0
	endif
	widget_control,(*pState).slider,get_value = index
	index = index[0] - 1
	if index eq -1 then begin
  		widget_control,plotBase,set_uvalue = pState,/no_copy
  		return,0
	endif
	qty = (*(*(*pState).value)[0]).qty[*,index]
	err = (*(*(*pState).value)[0]).err[*,index]
	x = (*(*(*pState).value)[0]).x

	filename = DIALOG_PICKFILE(dialog_parent = event.id,filter = '*.txt',$
	                           /write,title = 'Output file name')
	filename = filename+'.txt'
	openw,lun,filename,/get_lun

	; how many groups are we printing out?
	npts = n_elements(x)
	for i = 0,(*pState).npointers-1 do begin
		qty = (*(*(*pState).value)[i]).qty[*,index]
		err = (*(*(*pState).value)[i]).err[*,index]
		x = (*(*(*pState).value)[i]).x
		y = (*(*(*pState).value)[i]).y[index]
		printf,lun,'Data Set: ',strcompress(string(i))
		printf,lun,'Group: ',strcompress(string(y))
		for j = 0,npts-1 do begin
	  		printf,lun,x[j],qty[j],err[j]
		endfor
	endfor
	free_lun,lun
	end
else:
endcase
ret = 0
widget_control,plotBase,set_uvalue = pState,/no_copy
return,ret
end
;;;;;;;;;;;;;
function rmd_widget_draw,parent,$
                    			VALUE = value,$
                    			UVALUE = uvalue,$
                    			UNAME = uname,$
                    			XSIZE = xsize,$
                    			YSIZE = ysize,$
                    			SAVE_DATA = save_data,$
                    			TRACK_CURSOR = track_cursor

compile_opt hidden
; widget definition module
if not keyword_set(uvalue) then uvalue = 0
if not keyword_set(uname) then uname = "rmd_widget_draw_uname"
if not keyword_set(xsize) then xsize = 450
if not keyword_set(ysize) then ysize = 400
if not keyword_set(track_cursor) then $
					track_cursor = 0 else track_cursor = 1

if n_tags(value) eq 0 then begin

  dataStr = {qty:0.0,$
  			 err:0.0,$
  			 x:0.0,$
  			 y:0.0,$
             xlabel:"",$
             ylabel:"",$
             title:""}
  datPtr = ptr_new(dataStr,/no_copy)
  value = ptr_new([datPtr],/no_copy)
endif
window,/free,/pixmap,XSIZE = xsize,YSIZE = ysize
winPix = !d.window
device,decomposed = 0
colors = hfbs_GetColor(/Load, Start=1)

; Note that the order of the widgets matter here!
base = widget_base(parent,$
                          UVALUE = uvalue,$
                          UNAME = uname,$
                          func_get_value = "rmd_widget_draw_get_val",$
                          pro_set_val = "rmd_widget_draw_set_val",$
                          /col,/frame)

; we will use the user value for plotBase as the container
; for the state information
plotBase = widget_base(base,/col)
rowBase = widget_base(plotBase,/row,$
                          event_func = 'rmd_widget_draw_event')

slider = widget_slider(rowBase,max = 1,$
                       value = 1,title = "Group",$
                       event_pro = "rmd_widget_draw_changeGroup")
text = widget_label(rowBase,value = " ",xsize = 150,units = 0)


saveNames = ['SAVE','SAVE ALL',$
  			  'SAVE CURRENT']
if keyword_set(save_data) then begin
  desc = replicate({ flags:0, name:'' }, 3)
  desc.flags = [1,0,0]
  desc.name = saveNames
  dropmenu = cw_pdmenu(rowBase,desc,/return_index)
endif

if track_cursor eq 1 then me = 1 else me = 0
butWin = widget_draw(plotBase,xsize = xsize,ysize = ysize,/button_events,$
                          kill_notify='rmd_widget_draw_cleanup',$
                          notify_realize = 'rmd_widget_draw_realize',$
                          event_pro = 'rmd_widget_drawDraw',$
                          motion_events = me)

state = {value:value,$
         winVis:0L,$
         winPix:winPix,$
         colors:colors,$
         autoscale:1,$
         mouse:0B,$
         xviewrange:[0.0,1.0],$
         yviewrange:[0.0,1.0],$
         xbox:[0.0,1.0],$
         ybox:[0.0,1.0],$
         slider:slider,$
         butWin:butWin,$
         ngroups:0,$
         nPointers:0,$
         datPtr:datPtr,$
         text:text,$
         track_cursor:track_cursor}

pState = ptr_new(state,/no_copy)
; Here we will explicitly make PLOTBASE the container
; for all of the program info to be passed around
; i.e. the state info.
widget_control,plotBase,set_uvalue = pState,/no_copy
return,base
end
;;;;;;;;;;;;;
