;$Id: db_datacuts.pro,v 1.9 2006/08/29 20:22:41 azuah Exp $
;###############################################################################
;
; NAME:
;   DB_datacuts
;
; PURPOSE:
;   Modal widget dialog that gets the data cutting information from
;   the user, packages it up, and returns it to the calling program in
;   a structure.
;
; CATEGORY:
;   Data Browser
;
; 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
;   Dec, 2002
;
; 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.
;
;###############################################################################



;===============================================================================
; DB_DC_winEvents
;
; PURPOSE:
;   Event handler for events originating from the draw widget
;
; PARAMETERS:
;   event [in] - the event structure to be handled
;
pro DB_DC_winEvents,event
widget_control,event.top,get_uvalue = pState
case event.type of
0:	begin		; button press
	  (*pState).mouse = event.press
	  if (*pState).mouse eq 4 then begin	; right click...change width
	   coords = (convert_coord(event.x,event.y,/device,/to_data))
	   if (*pState).cutAxis eq 'x' then begin
	   		(*pState).width = 2.0*abs((*pState).center - coords[0])
	   endif else begin
	   		(*pState).width = 2.0*abs((*pState).center - coords[1])
	   endelse
	   id_width = widget_info(event.top,find_by_uname = 'CUT_WIDTH')
	   widget_control,id_width,set_value = strtrim(string((*pState).width),2)
	   DB_DC_plotRegion,event
	  endif
	  if (*pState).mouse eq 1 then begin	; left click...change center
	   if (*pState).cutAxis eq 'x' then begin
	     (*pState).center = (convert_coord(event.x,event.y,/device,/to_data))[0]
	   endif else begin
	   	 (*pState).center = (convert_coord(event.x,event.y,/device,/to_data))[1]
	   endelse
	   id_center = widget_info(event.top,find_by_uname = 'CUT_CENTER')
	   widget_control,id_center,set_value = strtrim(string((*pState).center),2)
	   DB_DC_plotRegion,event
	  endif
	end
1:	begin	; button release
	 if (*pState).mouse eq 1 then begin	; left click...change center
	   if (*pState).cutAxis eq 'x' then begin
	   	 (*pState).center = (convert_coord(event.x,event.y,/device,/to_data))[0]
	   endif else begin
	   	 (*pState).center = (convert_coord(event.x,event.y,/device,/to_data))[1]
	   endelse
	   id_center = widget_info(event.top,find_by_uname = 'CUT_CENTER')
	   widget_control,id_center,set_value = strtrim(string((*pState).center),2)
	   DB_DC_plotRegion,event
	  (*pState).mouse = 0B
	 endif
	 if (*pState).mouse eq 4 then begin; right click...change width
	   coords = (convert_coord(event.x,event.y,/device,/to_data))
	   if (*pState).cutAxis eq 'x' then begin
	   	 (*pState).width = 2.0*abs((*pState).center - coords[0])
	   endif else begin
	   	 (*pState).width = 2.0*abs((*pState).center - coords[1])
	   endelse
	   id_width = widget_info(event.top,find_by_uname = 'CUT_WIDTH')
	   widget_control,id_width,set_value = strtrim(string((*pState).width),2)
	   DB_DC_plotRegion,event
	  (*pState).mouse = 0B
	 endif
	end
2:	begin	; mouse motion
	  if (*pState).mouse eq 1 then begin
	   id_center = widget_info(event.top,find_by_uname = 'CUT_CENTER')
	   if (*pState).cutAxis eq 'x' then begin
	   	 (*pState).center = (convert_coord(event.x,event.y,/device,/to_data))[0]
	   endif else begin
	   	 (*pState).center = (convert_coord(event.x,event.y,/device,/to_data))[1]
	   endelse
	   widget_control,id_center,set_value = strtrim(string((*pState).center),2)
	   DB_DC_plotRegion,event
	  endif
	  if (*pState).mouse eq 4 then begin
	   coords = (convert_coord(event.x,event.y,/device,/to_data))
	   if (*pState).cutAxis eq 'x' then begin
	   		(*pState).width = 2.0*abs((*pState).center - coords[0])
	   endif else begin
	   		(*pState).width = 2.0*abs((*pState).center - coords[1])
	   endelse
	   id_width = widget_info(event.top,find_by_uname = 'CUT_WIDTH')
	   widget_control,id_width,set_value = strtrim(string((*pState).width),2)
	   DB_DC_plotRegion,event
	  endif
	end
else:
endcase
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_DC_updateFields
;
; PURPOSE:
;   Event handler. Update all fields in main dialog
;
; PARAMETERS:
;   event [in] - the event structure to be handled
;
pro DB_DC_updateFields,event
widget_control,event.top,get_uvalue = pState
id_sliderBase = widget_info(event.top,find_by_uname = 'CUT_SLIDER_BASE')
id_slider = widget_info(event.top,find_by_uname = 'CUT_SLIDER')
widget_control,id_slider,get_value = index & index = index[0] - 1
(*pState).center = (*(*pState).p_center)[index]
(*pState).width = (*(*pState).p_width)[index]
(*pState).cutAxis = (*(*pState).p_cutAxis)[index]
id_cutAxis = widget_info(event.top,find_by_uname = 'CUT_AXIS')
if (*pState).cutAxis eq 'x' then val = 0 else val = 1
widget_control,id_cutAxis,set_value = val
id_width = widget_info(event.top,find_by_uname = 'CUT_WIDTH')
widget_control,id_width,set_value = strtrim(string((*pState).width),2)
id_center = widget_info(event.top,find_by_uname = 'CUT_CENTER')
widget_control,id_center,set_value = strtrim(string((*pState).center),2)
DB_DC_plotRegion,event
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_DC_updateArrays
;
; PURPOSE:
;   Event handler. Retrieve current dialog settings and save them
;
; PARAMETERS:
;   event [in] - the event structure to be handled
;
pro DB_DC_updateArrays,event
widget_control,event.top,get_uvalue = pState
id_sliderBase = widget_info(event.top,find_by_uname = 'CUT_SLIDER_BASE')
id_slider = widget_info(event.top,find_by_uname = 'CUT_SLIDER')
widget_control,id_slider,get_value = index & index = index[0] - 1
(*(*pState).p_center)[index] = (*pState).center
(*(*pState).p_width)[index] = (*pState).width
(*(*pState).p_cutAxis)[index] = (*pState).cutAxis
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_DC_draw
;
; PURPOSE:
;   Event handler. Refresh draw window
;
; PARAMETERS:
;   event [in] - the event structure to be handled
;
pro DB_DC_draw,event
widget_control,event.top,get_uvalue = pState
xr = [min(*(*pState).xPtr),max(*(*pState).xPtr)]
yr = [min(*(*pState).yPtr),max(*(*pState).yPtr)]
if ((*pState).zipper) then begin
    xr = (*pState).xlimits
    yr = (*pState).ylimits
endif
wset,(*pState).winPix
plot,*(*pState).xPtr,*(*pState).yPtr,/nodata,xstyle = 1,ystyle = 1, $
	xtitle = (*pState).xLabel,ytitle = (*pState).yLabel,xrange = xr,yrange = yr
wset,(*pState).winVis
device,copy = [0,0,!d.x_size,!d.y_size,0,0,(*pState).winPix]
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_DC_getCenter
;
; PURPOSE:
;   Event handler. Retrieve the current cut center from dialog
;
; PARAMETERS:
;   event [in] - the event structure to be handled
;
pro DB_DC_getCenter,event
widget_control,event.top,get_uvalue = pState

id_center = widget_info(event.top,find_by_uname = 'CUT_CENTER')
widget_control,id_center,get_value = cen & cen = float(cen[0])
(*pState).center = cen
id_width = widget_info(event.top,find_by_uname = 'CUT_WIDTH')
widget_control,id_width,get_value = wid & wid = float(wid[0])
(*pState).width = wid
wset,(*pState).winVis
device,copy = [0,0,!d.x_size,!d.y_size,0,0,(*pState).winPix]
; x or y cut?
if (*pState).cutAxis eq 'x' then begin
  plots,[cen,cen],!y.crange,/data
endif else begin	; y axis
  plots,!x.crange,[cen,cen],/data
endelse
DB_DC_updateArrays,event
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_DC_getWidth
;
; PURPOSE:
;   Event handler. Retrieve the current cut width from dialog
;
; PARAMETERS:
;   event [in] - the event structure to be handled
;
pro DB_DC_getWidth,event
widget_control,event.top,get_uvalue = pState
id_center = widget_info(event.top,find_by_uname = 'CUT_CENTER')
widget_control,id_center,get_value = cen & cen = float(cen[0])
(*pState).center = cen
id_width = widget_info(event.top,find_by_uname = 'CUT_WIDTH')
widget_control,id_width,get_value = wid & wid = float(wid[0])
(*pState).width = wid
wset,(*pState).winVis
device,copy = [0,0,!d.x_size,!d.y_size,0,0,(*pState).winPix]
; x or y cut?
if (*pState).cutAxis eq 'x' then begin
  xlo = (*pState).center - 0.5*(*pState).width
  xhi = (*pState).center + 0.5*(*pState).width
  ylo = (!y.crange)[0]
  yhi = (!y.crange)[1]
  xbox = [xlo,xhi,xhi,xlo,xlo]
  ybox = [ylo,ylo,yhi,yhi,ylo]
  polyfill,xbox,ybox,/data
endif else begin	; y axis
  ylo = (*pState).center - 0.5*(*pState).width
  yhi = (*pState).center + 0.5*(*pState).width
  xlo = (!x.crange)[0]
  xhi = (!x.crange)[1]
  xbox = [xlo,xhi,xhi,xlo,xlo]
  ybox = [ylo,ylo,yhi,yhi,ylo]
  polyfill,xbox,ybox,/data
endelse
DB_DC_updateArrays,event
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_DC_plotRegion
;
; PURPOSE:
;   Event handler. Determine region to be plotted based on the current
;   cut center and width.
;
; PARAMETERS:
;   event [in] - the event structure to be handled
;
pro DB_DC_plotRegion,event
widget_control,event.top,get_uvalue = pState
id_width = widget_info(event.top,find_by_uname = 'CUT_WIDTH')
widget_control,id_width,get_value = val
if (*pState).cutAxis eq 'x' then begin
  r = (!x.crange)[1]-(!x.crange)[0]
endif else begin
  r = (!y.crange)[1]-(!y.crange)[0]
endelse
if float(val[0]) lt 1.e-2*r then begin
	DB_DC_getCenter,event
endif else begin
	DB_DC_getWidth,event
endelse
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_DC_handleNcuts
;
; PURPOSE:
;   Event handler. For the slider widgets.
;
; PARAMETERS:
;   event [in] - the event structure to be handled
;
pro DB_DC_handleNcuts,event
widget_control,event.top,get_uvalue = pState
id_ncuts = widget_info(event.top,find_by_uname = 'NCUTS')
widget_control,id_ncuts,get_value = val & val = fix(val[0])
if val le 0 then begin
	strout = ['Number of cuts must be greater than', $
			'or equal to 1']
	void = dialog_message(dialog_parent = event.top,strout)
	sliderBase = widget_info(event.top,find_by_uname = 'CUT_SLIDER_BASE')
	widget_control,sliderBase,map = 0
	return
endif
if val gt 1 then begin
	sliderBase = widget_info(event.top,find_by_uname = 'CUT_SLIDER_BASE')
	slider = widget_info(event.top,find_by_uname = 'CUT_SLIDER')
	widget_control,sliderBase,map = 1
	widget_control,slider,set_slider_max = val
	(*pState).ncuts = val
	*(*pState).p_center = fltarr((*pState).ncuts)
	*(*pState).p_width = fltarr((*pState).ncuts)
	*(*pState).p_cutAxis = replicate((*pState).cutAxis,(*pState).ncuts)
endif else begin
	sliderBase = widget_info(event.top,find_by_uname = 'CUT_SLIDER_BASE')
	widget_control,sliderBase,map = 0
	(*pState).ncuts = 1
	*(*pState).p_center = fltarr((*pState).ncuts)
	*(*pState).p_width = fltarr((*pState).ncuts)
	*(*pState).p_cutAxis = (*pState).cutAxis;strarr((*pState).ncuts)
endelse
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_DC_getValues
;
; PURPOSE:
;   Event handler. Retrieve current stored values specifying the cut.
;
; PARAMETERS:
;   event [in] - the event structure to be handled
;
pro DB_DC_getValues,event
widget_control,event.top,get_uvalue = pState
oContainer = (*pState).oContainer
oall = oContainer->get(/all)
;void = oall[(*pState).fileIndex]->get(dataref = o)
r = (oall[(*pState).fileIndex])->get(xVal = xVal,	$
			yVal = yVal,	$
			xLabel = xLabel,	$
			yLabel = yLabel	)
(*pState).xlabel = xLabel
(*pState).ylabel = yLabel
*(*pState).xPtr = xVal
*(*pState).yPtr = yVal
DB_DC_draw,event
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_DC_changeFiles
;
; PURPOSE:
;   Event handler. The dataset has changed.
;
; PARAMETERS:
;   event [in] - the event structure to be handled
;
pro DB_DC_changeFiles,event
widget_control,event.top,get_uvalue = pState
(*pState).fileIndex = event.index[0]
(*pState).fileOut = (*pState).filenames[event.index[0]]
DB_DC_getValues,event
DB_DC_changeCutAxis,event
DB_DC_plotRegion,event
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_DC_changeCutAxis
;
; PURPOSE:
;   Event handler. The cut axis has changed.
;
; PARAMETERS:
;   event [in] - the event structure to be handled
;
pro DB_DC_changeCutAxis,event
widget_control,event.top,get_uvalue = pState
id_cutAxis = widget_info(event.top,find_by_uname = 'CUT_AXIS')
widget_control,id_cutAxis,get_value = val
val = val[0]
if val eq 0 then caxis = 'x' else caxis = 'y'
(*pState).cutAxis = caxis
DB_DC_updateArrays,event
DB_DC_updateFields,event
DB_DC_plotRegion,event
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_DC_Events
;
; PURPOSE:
;   Event handler. Main event handler.
;
; PARAMETERS:
;   event [in] - the event structure to be handled
;
pro DB_DC_events,event
widget_control,event.top,get_uvalue = pState
uname = widget_info(event.id,/uname)
case uname of
'NCUTS': $
		begin
			DB_DC_handleNcuts,event
		end
'CUT_AXIS': $
		begin
			DB_DC_changeCutAxis,event
		end
'CUT_WIDTH': $
		begin
			DB_DC_plotRegion,event
		end
'CUT_CENTER': $
		begin
			DB_DC_plotRegion,event
		end
'CUT_SLIDER': $
		begin
			DB_DC_updateFields,event
		end
'DATASELECTION': $
		begin
			DB_DC_changeFiles,event
		end
'ACCEPT': $
		begin
			; First determine if a valid cut was made
;                        if (*(*pstate).p_center eq 0.) and $
;				(*(*pstate).p_width eq 0.) then (*pstate).cancel = 1
                        res = where(*(*pstate).p_center eq 0.,cnt1)
                        res = where(*(*pstate).p_width eq 0.,cnt2)
                        if ((cnt1 ge 0) and (cnt2 gt 0)) then (*pstate).cancel = 1
			widget_control,event.top,/destroy
		end
'CANCEL': $
		begin
			(*pState).cancel = 1
			widget_control,event.top,/destroy
		end
else:
endcase

end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_dataCuts
;
; PURPOSE:
;   Modal widget dialog that gets the data cutting information from
;   the user, packages it up, and returns it to the calling program in
;   a structure.
;
; PARAMETERS:
;   oContainer [in;req] - A container of dataset objects to be cut
;
; KEYWORDS:
;   group_leader [in] - the ID of the parent widget from which this
;                       dialog was launched.
;
;   zipper [in] - Used to indicate that the cut data are to be zipped.
;
; RETURN VALUE
;   A structure containing the cut parameters specified by the user.
;     CANCEL:   0 if false, 1 if true
;     FILEOUT:  string variable
;     CENTER:   array of centers for final cuts
;     WIDTH:    array of widths for final cuts
;     CUTAXIS:  array of cut axes: 'x' or 'y'
;
function DB_DataCuts,	oContainer,group_leader=group_leader,zipper=zipper

; Pull out all of the "available" files
xminmax = 0.0
xmaxmin = 0.0
yminmax = 0.0
ymaxmin = 0.0
oall = oContainer->get(/all)
n_objects = oContainer->count()
for i = 0,n_objects-1 do begin
    class = obj_class(oall[i])
    if class ne 'DAVEDAT2D' then return,{cancel:0}
                                ;void = oall[i]->get(dataref = o)
    r = oall[i]->get(nameTag=nameTag,xVal=xVal,yVal=yVal)
    if i eq 0 then begin
        nameTags = nameTag
        nx = n_elements(xVal)
        ny = n_elements(yVal)
    endif else begin
        nameTags = [nameTags,nameTag]
        nx = [nx,n_elements(xVal)]
        ny = [ny,n_elements(yVal)]
    endelse
    if (keyword_set(zipper)) then begin
        if (i eq 0) then begin
            xminmax = min(xVal,max=xmaxmin)
            yminmax = min(yVal,max=ymaxmin)
        endif else begin
            xmin = min(xVal,max=xmax)
            ymin = min(yVal,max=ymax)
            xminmax = xminmax > xmin
            xmaxmin = xmaxmin < xmax
            yminmax = yminmax > ymin
            ymaxmin = ymaxmin < ymax 
        endelse 
        if ((xmaxmin le xminmax) or (ymaxmin le yminmax)) then begin
            title = 'DATABROWSER - Zipper'
            message = 'Insufficient overlap between datasets hence they cannot be combined!'
            void = dialog_message(message,/error,title=title,dialog_parent=group_leader)
            return, 0
        endif
    endif
endfor
nx = nx[0]
ny = ny[0]

; Widget definition module
if n_elements(group_leader) eq 0 then begin
	group_leader = 0L
	modal = 0
endif else begin
	modal = 1
endelse

tlb = widget_base(group_leader = group_leader, /row, $
	title = 'Data Cutting Interface',modal = modal)
ctrlBase = widget_base(tlb,/col,/base_align_right)
selections = ['select 2D Data',nameTags]
drop = widget_droplist(ctrlBase,value = nameTags,uname = 'DATASELECTION')


fsize = 20
ncuts = cw_field(ctrlBase,title = 'number of cuts',/row,value = '1',/string, $
		uname = 'NCUTS',/return_events,xsize = fsize)

cutAxisName = ['x','y']
void = cw_bgroup(ctrlBase,cutAxisName,/row,/exclusive, $
	label_left = 'cut axis',/no_release, $
	frame = 1,set_value = 0,/return_index, $
	uname = 'CUT_AXIS')

void = cw_field(ctrlBase,title = 'cut center',/row,value = '0.0',/string, $
		uname = 'CUT_CENTER',/return_events,xsize = fsize)
void = cw_field(ctrlBase,title = 'cut width',/row,value = '0.0',/string, $
		uname = 'CUT_WIDTH',/return_events,xsize = fsize)
sliderBase = widget_base(ctrlBase,/col,uname = 'CUT_SLIDER_BASE',map = 0)
void = widget_slider(sliderBase,title = 'cut number',value = 1, $
		maximum = 2,minimum = 1,uname = 'CUT_SLIDER')

void = widget_label(ctrlBase,value= 'After editing a text field, you must use')
void = widget_label(ctrlBase,value= 'carriage return to keep modified value!')
rowBase = widget_base(ctrlBase,/row,/align_center)
void = widget_button(rowBase,value = 'Accept',uname = 'ACCEPT')
void = widget_button(rowBase,value = 'Cancel',uname = 'CANCEL')

xsize = 300 & ysize = 300
win = widget_draw(tlb,/button_events,/motion_events, $
		event_pro = 'DB_DC_winEvents',xsize = xsize,ysize = ysize)

widget_control,tlb,/realize

; Zipper specific
sensitive = (keyword_set(zipper))? 0 : 1
widget_control, drop, sensitive=sensitive
widget_control, ncuts,sensitive=sensitive

widget_control,win,get_value = winVis
window,/free,xsize = xsize,ysize = ysize,/pixmap
winPix = !d.window
state = {	win:win,							$
			winVis:winVis, 						$
			winPix:winPix, 						$
			cancel:0, 							$
			filenames:nameTags,				$
			fileOut:nameTags[0], 				$
			cutAxis:'x',						$
			xPtr:ptr_new(/allocate_heap),		$
			yPtr:ptr_new(/allocate_heap),		$
                        zipper:keyword_set(zipper), $
                        xlimits:[xminmax,xmaxmin], $
                        ylimits:[yminmax,ymaxmin], $
			ncuts:1,							$
			center:0.0,							$
			width:0.0,							$
			p_width:ptr_new(/allocate_heap),	$
			p_center:ptr_new(/allocate_heap),	$
			p_cutAxis:ptr_new(/allocate_heap),		$
			mouse:0B,							$
			xLabel:'',							$
			yLabel:'',							$
			fileIndex:0,						$
			oContainer:oContainer 				$
		}

pState = ptr_new(state,/no_copy)
widget_control,tlb,set_uvalue = pState

ev = {event,id:void,top:tlb,handler:0L}
DB_DC_handleNcuts,ev
DB_DC_getValues,ev
DB_DC_plotRegion,ev

registerName = 'db_dc'
xmanager,registerName,tlb,/no_block,event_handler = 'DB_DC_events'
DB_DC_structure = 	{	cancel:(*pState).cancel,	$
						fileOut:(*pState).fileOut,	$
						center:*(*pState).p_center,	$
						width:*(*pState).p_width,		$
						cutAxis:*(*pState).p_cutAxis	$
					}

wdelete,(*pState).winPix
ptr_free,(*pState).xPtr,(*pState).yPtr
ptr_free,(*pState).p_center,(*pState).p_width,(*pState).p_cutAxis
ptr_free,pState
return,DB_DC_structure
end
