; $Id: ft_toolkit.pro,v 1.6 2005/02/08 18:51:08 dimeo Exp $
;###############################################################################
;
; NAME:
;  FT_TOOLKIT
;
; PURPOSE:
;  Main driver application for the FT Toolkit.
;
; CATEGORY:
;  DAVE, Data Analysis, FT Toolkit.
;
; 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
;
; 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.
;
;###############################################################################
; NAME:
;
;  FT_TOOLKIT__DEFINE
;
; PURPOSE:
;
;  Object class for the Fourier Transform Toolkit utility.
;  This is an object widget.
;
; AUTHOR:
;
;  Robert Dimeo
;  National Institute of Standards and Technology
;  Center for Neutron Research
;  100 Bureau Drive, Mail Stop 8562
;  Gaithersburg, MD 20899
;  Tel: (301) 975-8135
;  Email: robert.dimeo@nist.gov
;
; CATEGORY:
;
;  DAVE application
;
; INPUT PARAMETERS:
;
;  GROUP_LEADER --
;  DIRECTORY --
;  NOTIFY_IDS --
;
; REQUIRED PROGRAMS:
;
;  FT_WIN__DEFINE
;  FSC_COLOR
;
; -
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pro ft_toolkit::cleanup
if obj_valid(self.sig_obj) then obj_destroy,self.sig_obj
if obj_valid(self.res_obj) then obj_destroy,self.res_obj
if obj_valid(self.corr_obj) then obj_destroy,self.corr_obj
if obj_valid(self.decon_obj) then obj_destroy,self.decon_obj
tvlct,*self.rptr,*self.gptr,*self.bptr
ptr_free,self.rptr,self.gptr,self.bptr,self.color_ptr
if (*self.sample_win_ptr).winpix ne 0L then $
   wdelete,(*self.sample_win_ptr).winpix
if (*self.res_win_ptr).winpix ne 0L then $
   wdelete,(*self.res_win_ptr).winpix
if (*self.decon_win_ptr).winpix ne 0L then $
   wdelete,(*self.decon_win_ptr).winpix
heap_free,self.sample_win_ptr
heap_free,self.res_win_ptr
heap_free,self.decon_win_ptr
device,decomposed = self.dc
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pro ft_toolkit_cleanup,tlb
widget_control,tlb,get_uvalue = self
obj_destroy,self
;help,/heap,/brief
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::quit,event
widget_control,self.tlb,/destroy
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::get_tab,event,name = name
; Returns the id of the current selected tab.
; ID = SELF->GET_TAB(EVENT)
tab_id = widget_info(event.top,find_by_uname = 'ft_tab')
tab = widget_info(tab_id,/tab_current)
uname = strupcase(self.tab_titles[tab])
name = uname
return,widget_info(event.top,find_by_uname = uname)
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::plot_info,event
tab_id = self->get_tab(event,name = name)
name = strupcase(name)
if name eq 'SAMPLE' then begin
   o = self.sig_obj
   autoscale = (*self.sample_win_ptr).autoscale
   xrange = (*self.sample_win_ptr).xrange
   yrange = (*self.sample_win_ptr).yrange
   if ~obj_valid(o) then return,0
endif
if name eq 'RESOLUTION' then begin
   o = self.res_obj
   if ~obj_valid(o) then return,0
   autoscale = (*self.res_win_ptr).autoscale
   xrange = (*self.res_win_ptr).xrange
   yrange = (*self.res_win_ptr).yrange
endif
if name eq 'DECONVOLUTION' then begin
   o = self.decon_obj
   if ~obj_valid(o) then return,0
   autoscale = (*self.decon_win_ptr).autoscale
   xrange = (*self.decon_win_ptr).xrange
   yrange = (*self.decon_win_ptr).yrange
endif
decon = name eq 'DECONVOLUTION'
slider_id = widget_info(event.top,find_by_uname = name+'GROUP_SLIDER')
plot_id = widget_info(event.top,find_by_uname = name+'PLOT_VARIABLE')
scat_id = widget_info(event.top,find_by_uname = name+'SCATTERING_FUNCTION')
scale_id = widget_info(event.top,find_by_uname = name+'PLOT_SCALE')
xscale_id = widget_info(event.top,find_by_uname = name+'X_SCALE')
widget_control,xscale_id,get_value = xscal_val
if xscal_val[0] eq 0 then xlog = 0B else xlog = 1B
widget_control,scale_id,get_value = scale_val
if scale_val[0] eq 0 then ylog = 0B else ylog = 1B

widget_control,slider_id,get_value = slider_val
index = slider_val - 1
widget_control,plot_id,get_value = plot_val
widget_control,scat_id,get_value = scat_val
plot_sel_name = self.plot_names[plot_val[0]]
if decon then scat_sel_name = 'I(Q,t)' else $
   scat_sel_name = self.scat_names[scat_val[0]]


ret = o->set_plot_info     (  energy = (scat_sel_name eq 'S(Q,E)'),    $
                              time = (scat_sel_name eq 'I(Q,t)'),      $
                              index = index,                         $
                              real = (plot_sel_name eq 'real'),      $
                              imag = (plot_sel_name eq 'imaginary'), $
                              phase = (plot_sel_name eq 'phase'),    $
                              magnitude = (plot_sel_name eq 'magnitude'),  $
                              xrange = xrange,                       $
                              yrange = yrange,                       $
                              autoscale = autoscale                  )

ret = o->draw(psym = -4,ylog = ylog,/nodata, $
      background = self.white,color = self.black, $
      overplot = 0B,xlog = xlog)
ret = o->draw(psym = -4,ylog = ylog,overplot = 1B, $
      color = self.blue,xlog = xlog)
if (scat_sel_name eq 'S(E)') and (o ne self.decon_obj) then begin
   e_low_id = widget_info(event.top,find_by_uname = name+'E_LOW')
   e_high_id = widget_info(event.top,find_by_uname = name+'E_HIGH')
   widget_control,e_low_id,get_value = e_low & e_low = float(e_low)
   widget_control,e_high_id,get_value = e_high & e_high = float(e_high)
   plots,[e_low,e_low],!y.crange,/data,thick = 2.0,linestyle = 2, $
      color = self.red
   plots,[e_high,e_high],!y.crange,/data,thick = 2.0,linestyle = 2, $
      color = self.red
endif
case name of
'SAMPLE':   $
   begin
      *(*self.sample_win_ptr).xptr = !x
      *(*self.sample_win_ptr).yptr = !y
   end
'RESOLUTION':   $
   begin
      *(*self.res_win_ptr).xptr = !x
      *(*self.res_win_ptr).yptr = !y
   end
'DECONVOLUTION':   $
   begin
      *(*self.decon_win_ptr).xptr = !x
      *(*self.decon_win_ptr).yptr = !y
   end
else:
endcase
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::plot_data,event
tab_id = self->get_tab(event,name = name)
case name of
'SAMPLE':   $
   begin
      winpix = (*self.sample_win_ptr).winpix
      winvis = (*self.sample_win_ptr).winvis
   end
'RESOLUTION':   $
   begin
      winpix = (*self.res_win_ptr).winpix
      winvis = (*self.res_win_ptr).winvis
   end
'DECONVOLUTION':   $
   begin
      winpix = (*self.decon_win_ptr).winpix
      winvis = (*self.decon_win_ptr).winvis
   end
else: return,0
endcase
wset,winpix
ret = self->plot_info(event)
wset,winvis
device,copy = [0,0,!d.x_size,!d.y_size,0,0,winpix]
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::scale,event
widget_control,event.id,get_value = val_scale
tab_id = self->get_tab(event,name = name)
case name of
'SAMPLE':         o = self.sig_obj
'RESOLUTION':     o = self.res_obj
'DECONVOLUTION':  o = self.decon_obj
else: return,0
endcase
if ~obj_valid(o) then return,0
if val_scale eq 1 then begin
   ret = o->scale_to_one()
endif else begin
   ret = o->original_scaling()
endelse
ret = self->plot_data(event)
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::save_dave,event
; Which tab are we looking at?
tab_id = self->get_tab(event,name = name)
case name of
'SAMPLE':   $
   begin
      o = self.sig_obj
      if ~obj_valid(o) then return,0
   end
'RESOLUTION':   $
   begin
      o = self.res_obj
      if ~obj_valid(o) then return,0
   end
'DECONVOLUTION':   $
   begin
      o = self.decon_obj
      if ~obj_valid(o) then return,0
   end
else: return,0
endcase
ret = o->get_property(display_name = name,dqty_time = dqty_time)

if size(dqty_time,/tname) ne 'DCOMPLEX' then begin
   strout = strarr(3)
   strout[0] = 'You must use the Monte-Carlo error estimate'
   strout[1] = 'for both the sample and resolution prior to'
   strout[2] = 'saving as DAVE files.  Returning...'
   void = dialog_message(dialog_parent = event.top, strout)
   return,0
endif
file1 = name+'_time_real.dave'
file2 = name+'_time_imaginary.dave'
file3 = name+'_time_phase.dave'
file4 = name+'_time_mag.dave'
strout = [  'Four data files will be written out with the following', $
            'names:',file1,file2,file3,file4]
void = dialog_message(dialog_parent = event.top,strout,/information)
dir = self.directory
filename = dialog_pickfile(dialog_parent = event.top, $
   path = dir,filter = '*.dave',/write,file = name)
; Do the error checking
if filename eq '' then return,0
ret = o->save_dave(filename)
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::process_root,event,name,o, $
   NO_MC_CALC = no_mc_calc
if n_elements(no_mc_calc) eq 0 then no_mc_calc = 0B
; Subtract off a function
if ~obj_valid(o) then return,0
ret = o->get_property(sub_array = sub_array)
ret = o->read_dave()
ret = o->set_property(sub_array = sub_array)
sub_fun_id = widget_info(event.top,find_by_uname = name+'SUB_FUN')
widget_control,sub_fun_id,get_value = expr & expr = expr[0]
if expr ne '' and ~lmgr(/vm) then begin
   ret = o->get_property(energy = e)
   ret = execute('sub='+expr,1)
   if ret eq 0 then return,0
   ret = o->get_property(qty_energy = qty_energy,ny = ny)
   if ny gt 1 then begin
      qty_energy = qty_energy - sub#(1+bytarr(ny))
   endif else begin
      qty_energy = qty_energy - sub
   endelse
   ret = o->set_property(qty_energy = qty_energy)
endif

; Subtract off a line with the appropriate slope and offset
; as specified by the user.
ret = o->get_property(  sub_array = sub_array,     $
                        qty_energy = qty_energy,   $
                        ny = ny,                   $
                        energy = e                 )
uy = 1+bytarr(ny)
if ny gt 1 then begin
   nx = n_elements(e)
   ux = 1+bytarr(nx)
   e_matrix = uy##e
   slope = ux#(reform(sub_array[0,*]))
   offset = ux#(reform(sub_array[1,*]))
   sub = slope*e_matrix+offset
   n21 = nx/2-1
   sub_matrix = shift(float(sub),-n21,0)
   qty_energy = qty_energy - sub_matrix
   ret = o->set_property(qty_energy = qty_energy)
endif else begin
   nx = n_elements(e)
   ux = 1+bytarr(nx)
   n21 = nx/2-1
   slope = sub_array[0] & offset = sub_array[1]
   sub_matrix = e*slope+offset
   qty_energy = qty_energy-shift(float(sub_matrix),-n21)
   ret = o->set_property(qty_energy = qty_energy)
endelse


e_low_id = widget_info(event.top,find_by_uname = name+'E_LOW')
e_high_id = widget_info(event.top,find_by_uname = name+'E_HIGH')
widget_control,e_low_id,get_value = elo & elo = double(elo[0])
widget_control,e_high_id,get_value = ehi & ehi = double(ehi[0])
ret = o->set_property(range = [elo,ehi])

; Pre-filter?
pre_filter_id = widget_info(event.top,find_by_uname = name+'PRE_FILTER')
pre_pixels_id = widget_info(event.top,find_by_uname = name+'PRE_FILTER_PIXELS')
pre_degree_id = widget_info(event.top,find_by_uname = name+'PRE_FILTER_DEGREE')
widget_control,pre_degree_id,get_value = degree & degree = fix(degree[0])
widget_control,pre_pixels_id,get_value = num_pixels_pre
num_pixels_pre = fix(num_pixels_pre[0])
;widget_control,pre_filter_id,get_value = pre_filter_value
pre_filter_value = widget_info(pre_filter_id,/droplist_select)
if pre_filter_value eq 0 then begin
   ret = o->boxcar_average(num_pixels_pre,/energy)
endif
if pre_filter_value eq 1 then begin
   ret = o->gaussian_smooth(num_pixels_pre,/energy)
endif
if pre_filter_value eq 2 then begin
   if num_pixels_pre lt degree then begin
      msg = 'Filter degree must be less than the filter width (# points)'
      void = dialog_message(dialog_parent = event.top,msg)
      return,0
   endif
   ret = o->savitzky_golay(num_pixels_pre,num_pixels_pre,degree,/energy)
endif
if (pre_filter_value eq 3) and (num_pixels_pre gt 2) then begin
   ret = o->hanning_smooth(num_pixels_pre,/energy)
endif
; Take the inverse Fourier transform to calculate I(t) from I(E)
ret = o->reverse_fft()

error_bars_id = widget_info(event.top,find_by_uname = name+'CALC_ERROR_BARS')
error_bars = widget_info(error_bars_id,/button_set)
if error_bars then begin
   if ~no_mc_calc then begin
      nmc_id = widget_info(event.top,find_by_uname = name+'NMC')
      widget_control,nmc_id,get_value = nmc & nmc = fix(nmc[0])
      ret = o->set_property(nmc = nmc)
      ret = o->mc_estimate_errors()
   endif
endif else begin
   ret = o->remove_error_bars()
endelse

; Post-filter?
post_filter_id = widget_info(event.top,find_by_uname = name+'POST_FILTER')
post_pixels_id = widget_info(event.top,find_by_uname = name+'POST_FILTER_PIXELS')
post_degree_id = widget_info(event.top,find_by_uname = name+'POST_FILTER_DEGREE')
widget_control,post_degree_id,get_value = degree & degree = fix(degree[0])
widget_control,post_pixels_id,get_value = num_pixels_post
num_pixels_post = fix(num_pixels_post[0])
;widget_control,post_filter_id,get_value = post_filter_value
post_filter_value = widget_info(post_filter_id,/droplist_select)
if post_filter_value eq 0 then begin
   ret = o->boxcar_average(num_pixels_post,/time)
endif
if post_filter_value eq 1 then begin
   ret = o->gaussian_smooth(num_pixels_post,/time)
endif
if post_filter_value eq 2 then begin
   if num_pixels_post lt degree then begin
      msg = 'Filter degree must be less than the filter width (# points)'
      void = dialog_message(dialog_parent = event.top,msg)
      return,0
   endif
   ret = o->savitzky_golay(num_pixels_post,num_pixels_post,degree,/time)
endif
if (post_filter_value eq 3) and (num_pixels_post gt 2) then begin
   ret = o->hanning_smooth(num_pixels_post,/energy)
endif
max_scale_id = widget_info(event.top,find_by_uname = name+'MAX_SCALE')
widget_control,max_scale_id,get_value = val
if val eq 1 then begin
   ret = o->scale_to_one()
endif else begin
   ret = o->original_scaling()
endelse
ret = self->plot_data(event)
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::process_deconvolution,event
; Check if both the sample and the resolution function have been
; defined.  If not both then get out.
if ~(obj_valid(self.sig_obj) and obj_valid(self.res_obj)) then $
   return,0

msg = 'Performing deconvolution'
title = 'Progress'
progressBar = Obj_New("SHOWPROGRESS",message = msg,title = title, $
   event.top)
progressBar->Start

ret = self.sig_obj->get_property(time = sig_time,           $
                                 qty_time = sig_qty_time,   $
                                 dqty_time = sig_dqty_time  )
ret = self.res_obj->get_property(time = res_time,           $
                                 qty_time = res_qty_time,   $
                                 dqty_time = res_dqty_time  )

; Create a Boolean variable that determines whether or not we
; should propagate the errors in I(Q,t)
prop_errors =  (n_elements(sig_dqty_time) gt 1) and $
               (n_elements(res_dqty_time) gt 1)

; Determine the overlap of the time vectors
tlo_res = min(res_time,max = thi_res)
tlo_sig = min(sig_time,max = thi_sig)
tlo = tlo_res > tlo_sig
thi = thi_res < thi_sig
t_index = where((sig_time le thi) and (sig_time ge tlo),nt)
sig_time = sig_time[t_index]
sig_qty_time = sig_qty_time[t_index,*]
if n_elements(sig_dqty_time) gt 1 then $
   sig_dqty_time = sig_dqty_time[t_index,*]
; Now rebin the resolution data onto the same limited time
; axis as we have just defined
qty_size = size(sig_qty_time,/dimensions)
ny = qty_size[1]

; Declare the output arrays
qty_out = complexarr(nt,ny)
dqty_out = complexarr(nt,ny)
for i = 0,ny-1 do begin
   x_out = sig_time
   x_in = res_time
   z_in = reform(res_qty_time[*,i])
   z_in_re = float(z_in)
   z_in_im = imaginary(z_in)

   if prop_errors then begin
      dz_in = reform(res_dqty_time[*,i])
      dz_in_im = imaginary(dz_in)
      dz_in_re = float(dz_in)
   endif else begin
      dz_in = z_in
      dz_in_re = float(z_in)
      dz_in_im = imaginary(z_in)
   endelse
   ; Rebin the real part first...
   drebin,x_in,z_in_re,dz_in_re,x_out,z_out_re,dz_out_re,   $
      err=err,emsg=emsg,/points,/to_points
   ; ...now rebin the imaginary part
   x_in = res_time
   x_out = sig_time
   drebin,x_in,z_in_im,dz_in_im,x_out,z_out_im,dz_out_im,   $
      err=err,emsg=emsg,/points,/to_points
   qty_out[*,i] = complex(z_out_re,z_out_im)
   dqty_out[*,i] = complex(dz_out_re,dz_out_im)
   progressBar->Update, (i+1)*100/ny
endfor

progressBar->Destroy
Obj_Destroy, progressBar

; Now we have the two arrays with which we will divide to
; get the deconvoluted data.
u = sig_qty_time
du = sig_dqty_time
v = qty_out
dv = dqty_out
z = u/v
; Now create a new object
if obj_valid(self.decon_obj) then obj_destroy,self.decon_obj
ret = self.sig_obj->get_property(filename = filename)
self.decon_obj = obj_new('ft_data',filename = filename,wid = event.top)

if prop_errors then begin
   dz = z*sqrt((du/u)^2+(dv/v)^2)
   ret = self.decon_obj->set_property( time = sig_time,  $
                                       qty_time = z,     $
                                       dqty_time = dz    )
endif else begin
   ret = self.decon_obj->set_property( time = sig_time,  $
                                       qty_time = z      )
endelse
; Update the slider
widget_control,self.decon_slider,set_slider_max = ny
name = 'DECONVOLUTION'
; Set the plot button so that |I(Q,t)| will be displayed
plot_id = widget_info(event.top,find_by_uname = name+'PLOT_VARIABLE')
widget_control,plot_id,set_value = 2
;scat_id = widget_info(event.top,find_by_uname = name+'SCATTERING_FUNCTION')
;widget_control,scat_id,set_value = 0

; Treat the data if necessary prior to the Fourier transformation
; Pre-filter?
o = self.decon_obj
ret = o->erase_energy()
pre_filter_id = widget_info(event.top,find_by_uname = name+'PRE_FILTER')
pre_pixels_id = widget_info(event.top,find_by_uname = name+'PRE_FILTER_PIXELS')
pre_degree_id = widget_info(event.top,find_by_uname = name+'PRE_FILTER_DEGREE')
widget_control,pre_degree_id,get_value = degree & degree = fix(degree[0])
widget_control,pre_pixels_id,get_value = num_pixels_pre
num_pixels_pre = fix(num_pixels_pre[0])
;widget_control,pre_filter_id,get_value = pre_filter_value
pre_filter_value = widget_info(pre_filter_id,/droplist_select)
if pre_filter_value eq 0 then begin
   ret = o->boxcar_average(num_pixels_pre,/time)
endif
if pre_filter_value eq 1 then begin
   ret = o->gaussian_smooth(num_pixels_pre,/time)
endif
if pre_filter_value eq 2 then begin
   if num_pixels_pre lt degree then begin
      msg = 'Filter degree must be less than the filter width (# points)'
      void = dialog_message(dialog_parent = event.top,msg)
      return,0
   endif
   ret = o->savitzky_golay(num_pixels_pre,num_pixels_pre,degree,/time)
endif
if (pre_filter_value eq 3) and (num_pixels_pre gt 2) then begin
   ret = o->hanning_smooth(num_pixels_pre,/time)
endif

;; Calculate S(Q,E)
;ret = o->forward_fft()
;
;; Post-filter?
;post_filter_id = widget_info(event.top,find_by_uname = name+'POST_FILTER')
;post_pixels_id = widget_info(event.top,find_by_uname = name+'POST_FILTER_PIXELS')
;post_degree_id = widget_info(event.top,find_by_uname = name+'POST_FILTER_DEGREE')
;widget_control,post_degree_id,get_value = degree & degree = fix(degree[0])
;widget_control,post_pixels_id,get_value = num_pixels_post
;num_pixels_post = fix(num_pixels_post[0])
;;widget_control,post_filter_id,get_value = post_filter_value
;post_filter_value = widget_info(post_filter_id,/droplist_select)
;if post_filter_value eq 0 then begin
;   ret = o->boxcar_average(num_pixels_post,/time)
;endif
;if post_filter_value eq 1 then begin
;   ret = o->gaussian_smooth(num_pixels_post,/time)
;endif
;if post_filter_value eq 2 then begin
;   if num_pixels_post lt degree then begin
;      msg = 'Filter degree must be less than the filter width (# points)'
;      void = dialog_message(dialog_parent = event.top,msg)
;      return,0
;   endif
;   ret = o->savitzky_golay(num_pixels_post,num_pixels_post,degree,/time)
;endif
;if (post_filter_value eq 3) and (num_pixels_post gt 2) then begin
;   ret = o->hanning_smooth(num_pixels_post,/time)
;endif

; Plot the data I(Q,t)
ret = self->plot_data(event)

return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::load_dave_data,event
; First, which tab are we in?  We need to distinguish between
; the sample tab and the resolution tab.
tab_id = self->get_tab(event,name = file_name) ; widget id of current tab
if strupcase(file_name) eq 'SAMPLE' then title = 'Select a sample file'
if strupcase(file_name) eq 'RESOLUTION' then title = 'Select a resolution file'
filename = dialog_pickfile(dialog_parent = event.top, $
   path = self.directory,filter = '*.dave',get_path = this_dir)
; Do the error checking
if filename eq '' then return,0
if ~file_test(filename) then return,0
self.directory = this_dir
ft_obj = obj_new('ft_data',filename = filename,wid = event.top)

if ~obj_valid(ft_obj) then begin
   strout = strarr(3)
   strout[0] = 'Error reading in DAVE data file'
   strout[1] = !error_state.msg
   strout[2] = 'Returning...'
   void = dialog_message(dialog_parent = event.top,strout)
   return,0
endif
ret = ft_obj->get_property(nx = nx,ny = ny,display_name = name)

if ret eq 0 then begin
   if obj_valid(ft_obj) then obj_destroy,ft_obj
   return,0
endif

if strupcase(file_name) eq 'SAMPLE' then begin
   if obj_valid(self.sig_obj) then obj_destroy,self.sig_obj
   self.sig_obj = ft_obj
   ret = self.sig_obj->get_property(energy = energy)
   elo = min(energy,max = ehi)
   de = energy[1]-energy[0]
   ret = ft_obj->set_property(range = [min(energy)-de,max(energy)+de])
   ret = ft_obj->get_property(range = range)
   this_format = '(f16.12)'
   elo_id = widget_info(event.top,find_by_uname = 'SAMPLEE_LOW')
   widget_control,elo_id,set_value = strtrim(string(elo,format = this_format),2)
   ehi_id = widget_info(event.top,find_by_uname = 'SAMPLEE_HIGH')
   widget_control,ehi_id,set_value = strtrim(string(ehi,format = this_format),2)
   widget_control,self.sample_slider,set_slider_max = ny
   ret = self->process_root(event,'SAMPLE',self.sig_obj)
endif
if strupcase(file_name) eq 'RESOLUTION' then begin
   if obj_valid(self.res_obj) then obj_destroy,self.res_obj
   self.res_obj = ft_obj
   ret = self.res_obj->get_property(energy = energy)
   elo = min(energy,max = ehi)
   de = energy[1]-energy[0]
   ret = ft_obj->set_property(range = [min(energy)-de,max(energy)+de])
   ret = ft_obj->get_property(range = range)
   this_format = '(f16.12)'
   elo_id = widget_info(event.top,find_by_uname = 'RESOLUTIONE_LOW')
   widget_control,elo_id,set_value = strtrim(string(elo,format = this_format),2)
   ehi_id = widget_info(event.top,find_by_uname = 'RESOLUTIONE_HIGH')
   widget_control,ehi_id,set_value = strtrim(string(ehi,format = this_format),2)
;   ret = self.resolution_slider->set_property(maximum = ny)
   widget_control,self.resolution_slider,set_slider_max = ny
   ret = self->process_root(event,'RESOLUTION',self.res_obj)
endif

ret = self->plot_data(event)

return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::load_example_files,event
; Set the sample tab first
tab_id = widget_info(event.top,find_by_uname = 'ft_tab')
widget_control,tab_id,set_tab_current = 0
sample_file = !DAVE_AUXILIARY_DIR+'20010618_01dyn.dave'
file_name = 'SAMPLE'
res_file = !DAVE_AUXILIARY_DIR+'20010529_01dyn.dave'
ft_obj = obj_new('ft_data',filename = sample_file,wid = event.top)
if ~obj_valid(ft_obj) then begin
   strout = strarr(3)
   strout[0] = 'Error reading in DAVE data file'
   strout[1] = !error_state.msg
   strout[2] = 'Returning...'
   void = dialog_message(dialog_parent = event.top,strout)
   return,0
endif
ret = ft_obj->get_property(nx = nx,ny = ny,display_name = name)

if ret eq 0 then begin
   if obj_valid(ft_obj) then obj_destroy,ft_obj
   return,0
endif

   if obj_valid(self.sig_obj) then obj_destroy,self.sig_obj
   self.sig_obj = ft_obj
   ret = self.sig_obj->get_property(energy = energy)
   elo = min(energy,max = ehi)
   de = energy[1]-energy[0]
   ret = ft_obj->set_property(range = [min(energy)-de,max(energy)+de])
   ret = ft_obj->get_property(range = range)
   this_format = '(f16.12)'
   elo_id = widget_info(event.top,find_by_uname = 'SAMPLEE_LOW')
   widget_control,elo_id,set_value = strtrim(string(elo,format = this_format),2)
   ehi_id = widget_info(event.top,find_by_uname = 'SAMPLEE_HIGH')
   widget_control,ehi_id,set_value = strtrim(string(ehi,format = this_format),2)
   widget_control,self.sample_slider,set_slider_max = ny
   ret = self->process_root(event,'SAMPLE',self.sig_obj)
   ret = self->plot_data(event)

; Set the current tab to the resolution tab
widget_control,tab_id,set_tab_current = 1
ft_obj = obj_new('ft_data',filename = res_file,wid = event.top)
if ~obj_valid(ft_obj) then begin
   strout = strarr(3)
   strout[0] = 'Error reading in DAVE data file'
   strout[1] = !error_state.msg
   strout[2] = 'Returning...'
   void = dialog_message(dialog_parent = event.top,strout)
   return,0
endif
ret = ft_obj->get_property(nx = nx,ny = ny,display_name = name)

if ret eq 0 then begin
   if obj_valid(ft_obj) then obj_destroy,ft_obj
   return,0
endif

   if obj_valid(self.res_obj) then obj_destroy,self.res_obj
   self.res_obj = ft_obj
   ret = self.res_obj->get_property(energy = energy)
   elo = min(energy,max = ehi)
   de = energy[1]-energy[0]
   ret = ft_obj->set_property(range = [min(energy)-de,max(energy)+de])
   ret = ft_obj->get_property(range = range)
   this_format = '(f16.12)'
   elo_id = widget_info(event.top,find_by_uname = 'RESOLUTIONE_LOW')
   widget_control,elo_id,set_value = strtrim(string(elo,format = this_format),2)
   ehi_id = widget_info(event.top,find_by_uname = 'RESOLUTIONE_HIGH')
   widget_control,ehi_id,set_value = strtrim(string(ehi,format = this_format),2)
;   ret = self.resolution_slider->set_property(maximum = ny)
   widget_control,self.resolution_slider,set_slider_max = ny
   ret = self->process_root(event,'RESOLUTION',self.res_obj)
   ret = self->plot_data(event)

return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::process_data,event,NO_MC_CALC = no_mc_calc
if keyword_set(no_mc_calc) then no_mc_calc = 1B else $
   no_mc_calc = 0B
; Which tab are we looking at?
tab_id = self->get_tab(event,name = file_name) ; widget id of current tab
if file_name eq 'SAMPLE' then $
   ret = self->process_root(event,'SAMPLE',self.sig_obj, $
      no_mc_calc = no_mc_calc)
if file_name eq 'RESOLUTION' then $
   ret = self->process_root(event,'RESOLUTION',self.res_obj, $
      no_mc_calc = no_mc_calc)
if file_name eq 'DECONVOLUTION' then $
   ret = self->process_deconvolution(event)
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::get_lin_parms,event,name,o
if ~obj_valid(o) then return,0
ret = o->get_property(ny = ny,sub_array = sub_array)
if ny gt 20 then begin
   msg = 'This option limited to 20 groups or less'
   void = dialog_message(dialog_parent = event.top,msg)
   return,0
endif
array = ft_detector_group_select(group_leader = event.top,  $
   ny = ny,sub_array = sub_array)
ret = o->set_property(sub_array = array)
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::specify_lin_parms,event
; Which tab are we looking at?
tab_id = self->get_tab(event,name = file_name) ; widget id of current tab
;print,'Signal: ',self.sig_obj
;print,'Resolution: ',self.res_obj
if file_name eq 'SAMPLE' then $
   ret = self->get_lin_parms(event,'SAMPLE',self.sig_obj)
if file_name eq 'RESOLUTION' then $
   ret = self->get_lin_parms(event,'RESOLUTION',self.res_obj)
ret = self->process_data(event)
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::change_tab_view,event
; Here we simply want to update the plot window with the
; appropriate data.
ret = self->plot_data(event)
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::build_data_controls,tab_base,name
; Build up the controls base.  This function was made to eliminate
; copying the same code twice, one for the sample and one for the
; resolution.
tab = widget_base(tab_base, title = name,/row, $
   uname = strupcase(name))
ctrl_base = widget_base(tab,/col)
if strupcase(name) eq 'DECONVOLUTION' then begin
   void = widget_button(ctrl_base,value = 'Deconvolute', $
      uvalue = {object:self,method:'process_deconvolution'})
endif else begin
   void = widget_button(ctrl_base,value = 'Load '+name+' Data', $
      uvalue = {object:self,method:'load_dave_data'})
endelse
name = strupcase(name)

decon = name eq 'DECONVOLUTION'

plot_names = ['real','imaginary','magnitude','phase']
self.plot_names = plot_names
void = cw_bgroup(ctrl_base,plot_names,/exclusive, /return_name, $
   label_top = 'What to plot',uname = name+'PLOT_VARIABLE',   $
   set_value = 0,/frame,/no_release, $
   uvalue = {object:self,method:'plot_data'})

if decon then scat_names = ['I(Q,t)'] else $
   scat_names = ['S(Q,E)','I(Q,t)']
self.scat_names = ['S(Q,E)','I(Q,t)']
void = cw_bgroup(ctrl_base,scat_names,/exclusive, /return_name, $
   label_top = 'Scattering Function', set_value = 0,   $
   uname = name+'SCATTERING_FUNCTION',/frame,/no_release, $
   uvalue = {object:self,method:'plot_data'})

scale_factor_names = ['None','I(t=0)=1']
scale_factor = cw_bgroup(ctrl_base,scale_factor_names,/exclusive, /return_name, $
   label_top = 'I(t) scaling', set_value = 1, /row,   $
   uname = name+'MAX_SCALE',/frame,/no_release, $
   uvalue = {object:self,method:'scale'})

scale_names = ['Linear scale','Log scale']
self.scale_names = scale_names
void = cw_bgroup(ctrl_base,scale_names,/exclusive, /return_name, $
   label_top = 'Y-axis scale', set_value = 0,   $
   uname = name+'PLOT_SCALE',/frame,/no_release, $
   uvalue = {object:self,method:'plot_data'})
void = cw_bgroup(ctrl_base,scale_names,/exclusive, /return_name, $
   label_top = 'X-axis scale', set_value = 0,   $
   uname = name+'X_SCALE',/frame,/no_release, $
   uvalue = {object:self,method:'plot_data'})
void = widget_button(ctrl_base,value = 'Save as DAVE', $
   uvalue = {object:self,method:'save_dave'})
; Now do the second column base control panel

limit_size = 20

this_xsize = 2
ctrl_base_2 = widget_base(tab,/col,uname = name+'CONTROL_BASE_2',/frame)
if ~decon then begin
   proc_base = widget_base(ctrl_base_2,/col,/frame)
   void = widget_label(proc_base,value = 'FT Processing Parameters')
   void = widget_button(proc_base,value = 'Linear BG slope and offsets',   $
      uvalue=  {object:self,method:'specify_lin_parms'})
   void = cw_field(proc_base,value = '0.0',  $
      title = 'Lower Energy Transform Limit',/col,  $
      uname = name+'E_LOW',/return_events,xsize = limit_size, $
      uvalue = {object:self,method:'process_data'})

   void = cw_field(proc_base,value = '1.0',  $
      title = 'Upper Energy Transform Limit',/col,  $
      uname = name+'E_HIGH',/return_events,xsize = limit_size, $
      uvalue = {object:self,method:'process_data'})

   void = cw_field(proc_base,value = '0.0*E',  $
      title = 'Subtraction Function',/col,  $
      uname = name+'SUB_FUN',/return_events,xsize = limit_size, $
      uvalue = {object:self,method:'process_data'})
endif
filter_base = widget_base(ctrl_base_2,/col,/frame)
void = widget_label(filter_base,value = 'Filtering Options')
filter_names = ['boxcar average','Gaussian blur','Savitzky-Golay','Hanning','None']
pre_filter_id = widget_droplist(filter_base,value = filter_names,  $
   title = 'Pre-filter',uname = name+'PRE_FILTER', $
   uvalue = {object:self,method:'process_data'})

void = cw_field(filter_base,value = '4',title = '# points (pre-filter)',/col,  $
   uname = name+'PRE_FILTER_PIXELS',/return_events,xsize = this_xsize, $
   uvalue = {object:self,method:'process_data'})
void = cw_field(filter_base,value = '4', $
   title = 'Savitzky-Golay filter order (pre-filter)',/col,  $
   uname = name+'PRE_FILTER_DEGREE',/return_events,xsize = this_xsize, $
   uvalue = {object:self,method:'process_data'})
if ~decon then begin
   post_filter_id = widget_droplist(filter_base,value = filter_names,  $
      title = 'Post-filter',uname = name+'POST_FILTER', $
      uvalue = {object:self,method:'process_data'})

   void = cw_field(filter_base,value = '4',title = '# points (post-filter)',/col,  $
      uname = name+'POST_FILTER_PIXELS',/return_events,xsize = this_xsize, $
      uvalue = {object:self,method:'process_data'})
   void = cw_field(filter_base,value = '4', $
      title = 'Savitzky-Golay filter order (post-filter)',/col,  $
      uname = name+'POST_FILTER_DEGREE',/return_events,xsize = this_xsize, $
      uvalue = {object:self,method:'process_data'})
endif
slider_id = widget_slider(ctrl_base_2,value = 1,title = 'Group',  $
   min = 1,max = 2,uname = name+'GROUP_SLIDER',/frame,/align_center, $
   uvalue = {object:self,method:'plot_data'})

slider_name = strupcase(widget_info(slider_id,/uname))

if slider_name eq 'SAMPLEGROUP_SLIDER' then self.sample_slider = slider_id
if slider_name eq 'RESOLUTIONGROUP_SLIDER' then self.resolution_slider = slider_id
if slider_name eq 'DECONVOLUTIONGROUP_SLIDER' then self.decon_slider = slider_id

if ~decon then begin
   mc_base = widget_base(ctrl_base_2,/col,/frame)
   non_ex_base = widget_base(mc_base,/nonexclusive,/row)
   error_bars_id = widget_button(non_ex_base,               $
      value = 'MC error estimate',   $
      uname = name+'CALC_ERROR_BARS', $
      uvalue = {object:self,method:'process_data'})
   void = cw_field(mc_base,/col,title = '# Monte-Carlo Steps', $
      /return_events,value = '50',uname = name+'NMC',xsize = 4, $
      uvalue = {object:self,method:'process_data'})
endif
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::zoom_events,event
tab_id = self->get_tab(event,name = name)
case name of
'SAMPLE':         win_ptr = self.sample_win_ptr
'RESOLUTION':     win_ptr = self.res_win_ptr
'DECONVOLUTION':  win_ptr = self.decon_win_ptr
else: return,0
end
if n_elements(*(*win_ptr).xptr) eq 0 then return,0
if n_elements(*(*win_ptr).yptr) eq 0 then return,0
case event.type of
0: begin    ; button press
      (*win_ptr).mouse = event.press
      if (*win_ptr).mouse eq 4 then begin
         (*win_ptr).autoscale = 1
         !x = *(*win_ptr).xptr & !y = *(*win_ptr).yptr
         ret = self->plot_data(event)
      endif
      if (*win_ptr).mouse eq 1 then begin
         (*win_ptr).xbox[0] = event.x
         (*win_ptr).ybox[0] = event.y
         !x = *(*win_ptr).xptr
         !y = *(*win_ptr).yptr
         ret = self->plot_data(event)
         empty
         (*win_ptr).autoscale = 0
      endif
   end
1: begin ; button release
    if (*win_ptr).mouse eq 1 then begin
      xll = (*win_ptr).xbox[0] < (*win_ptr).xbox[1]
      yll = (*win_ptr).ybox[0] < (*win_ptr).ybox[1]
      w = abs((*win_ptr).xbox[1] - (*win_ptr).xbox[0])
      h = abs((*win_ptr).ybox[1] - (*win_ptr).ybox[0])
      xur = xll + w
      yur = yll + h
      !x = *(*win_ptr).xptr & !y = *(*win_ptr).yptr
      ll = convert_coord(xll,yll,/device,/to_data)
      ur = convert_coord(xur,yur,/device,/to_data)
      (*win_ptr).xrange = [ll[0],ur[0]]
      (*win_ptr).yrange = [ll[1],ur[1]]
      ret = self->plot_data(event)
      (*win_ptr).mouse = 0B
    endif
    if (*win_ptr).mouse eq 4 then begin
      ret = self->plot_data(event)
      (*win_ptr).mouse = 0B
    endif
   end
2: begin ; mouse motion
      if (*win_ptr).mouse eq 1 then begin
         (*win_ptr).xbox[1] = event.x
         (*win_ptr).ybox[1] = event.y
         xc = [(*win_ptr).xbox[0],event.x,event.x,$
              (*win_ptr).xbox[0],$
              (*win_ptr).xbox[0]]
         yc = [(*win_ptr).ybox[0],(*win_ptr).ybox[0],$
              event.y,event.y,$
              (*win_ptr).ybox[0]]
         !x = *(*win_ptr).xptr & !y = *(*win_ptr).yptr
         wset,(*win_ptr).winvis
         device,copy = [0,0,!d.x_size,!d.y_size,0,0,(*win_ptr).winpix]
         plots,xc,yc,/device,color = self.green,thick = 2.0
         empty
      endif else begin
         ; Track mouse motion in device coordinates
         coords = convert_coord(event.x,event.y,/device,/to_data)
         wset,(*win_ptr).winvis
         device,copy =  [0,0,!d.x_size,!d.y_size,0,0,(*win_ptr).winpix]
         xpos = 0.15 & ypos = 0.95
         strout = 'cursor position = ( '+strtrim(string(coords[0]),2)+ ', '+ $
            strtrim(string(coords[1]),2)+ ' )'
         xyouts,xpos,ypos,strout,/normal,color = self.black
      endelse
   end
else:
endcase

return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::about,event
strout = ['Fourier Transform Toolkit','Written by R.M.Dimeo', $
          'March 26, 2004','','NIST Center for Neutron Research']
void = dialog_message(strout,/information,dialog_parent = event.top)
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::help,event
;strout = 'Documentation coming soon. -RMD'
;void = dialog_message(dialog_parent = event.top,strout,/info)
pdf_file = !DAVE_AUXILIARY_DIR+'ft_tools_docs.pdf'
void = launch_help(pdf_file,tlb = event.top)
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pro ft_toolkit_event,event
if dave_set_focus(event) then return
event_name = tag_names(event,/structure_name)   ; debugging
; General event handler for the object widget program
widget_control,event.id,get_uvalue = cmd
ret = call_method(cmd.method, cmd.object,event)
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::disclaimer
msg = strarr(19)
msg[0] = 'Disclaimer:'
msg[1] = ''
msg[2] = 'Warning: This application uses filtering and the'
msg[3] = 'Fast Fourier Transform (FFT) to treat data.'
msg[4] = 'There are numerous sources of systematic error'
msg[5] = 'associated with performing the Fourier Transformation'
msg[6] = 'on data.  By clicking "YES" below you are accepting'
msg[7] = 'complete responsibility for knowing all of the'
msg[8] = 'pitfalls and error sources associated with the FFT'
msg[9] = 'and you are responsible for all results that come from'
msg[10] = 'using this application to analyze your data.'
msg[11] = ''
msg[12] = 'You should read the accompanying documentation to gain'
msg[13] = 'an understanding of how to use the application but'
msg[14] = 'it must not be taken in lieu of a thorough'
msg[15] = 'understanding of the FFT.  There are numerous excellent'
msg[16] = 'texts on the FFT and you are expected to have working'
msg[17] = 'knowledge of this numerical technique and its'
msg[18] = 'suitability for your situation.'
title = 'Fourier Transform Toolkit Disclaimer'
void = dialog_message(dialog_parent = self.tlb,msg,/question,  $
   title = title)
if strupcase(void) eq 'NO' then ret = 0B else ret = 1B
return,ret
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::build_gui
;  Widget definition module
title = 'The DAVE Fourier Transform Toolkit'
if self.group_leader ne 0L then begin
   self.tlb = widget_base(/row,title = title,/tlb_frame_attr, $
      group_leader = self.group_leader,mbar = bar)
endif else begin
   self.tlb = widget_base(/row,title = title,/tlb_frame_attr, $
      mbar = bar)
endelse

filemenu = widget_button(bar,value = 'FILE',/menu)
void = widget_button(filemenu,value = 'Load example files', $
   uvalue = {method:'load_example_files',object:self})
void = widget_button(filemenu,value = 'Quit', $
   uvalue = {method:'quit',object:self})

help_menu = widget_button(bar,value = 'HELP',/menu)
void = widget_button(help_menu,value = 'PDF Documentation', $
   uvalue = {method:'help',object:self})
void = widget_button(help_menu,  $
   value = 'About the Fourier Transform Toolkit',  $
   uvalue = {method:'about',object:self})
; Create the tab widget bases
tab_base = widget_tab(self.tlb, uname = 'ft_tab',$
   uvalue = {object:self,method:'change_tab_view'},/multiline)
tab_titles = ['Sample','Resolution','Deconvolution']
self.tab_titles = tab_titles

ret = self->build_data_controls(tab_base,(tab_titles[0]))
ret = self->build_data_controls(tab_base,(tab_titles[1]))
ret = self->build_data_controls(tab_base,(tab_titles[2]))

; The deconvoluted data tab
;decon_tab = widget_base(tab_base,title = tab_titles[2],/row, $
;   uname = strupcase(tab_titles[2]))
;decon_ctrl_base = widget_base(decon_tab,/col)
;void = widget_button(decon_ctrl_base,value = 'Deconvolute', $
;   uvalue = {object:self,method:'process_deconvolution'})

win = widget_draw(self.tlb,xsize = 300,ysize = 300,   $
   /button_events,/motion_events,   $
   uvalue = {object:self,method:'zoom_events'})

tab_geom = widget_info(tab_base,/geometry)
ysize = fix(tab_geom.ysize) & xsize = fix(0.75*ysize)
widget_control,win,draw_ysize = ysize,draw_xsize = xsize
(*self.sample_win_ptr).win = win
(*self.res_win_ptr).win = win
(*self.decon_win_ptr).win = win


centertlb,self.tlb
widget_control,self.tlb,/realize

names = ['SAMPLEPRE_FILTER','SAMPLEPOST_FILTER', 'RESOLUTIONPRE_FILTER', $
   'RESOLUTIONPOST_FILTER','DECONVOLUTIONPRE_FILTER','DECONVOLUTIONPOST_FILTER']

for i = 0,n_elements(names)-1 do begin
   id = widget_info(self.tlb,find_by_uname = names[i])
   if widget_info(id,/valid_id) then begin
      num = widget_info(id,/droplist_number)
      widget_control,id,set_droplist_select = num-1
   endif
endfor


widget_control,win,get_value = winvis
(*self.sample_win_ptr).winvis = winvis
(*self.res_win_ptr).winvis = winvis
(*self.decon_win_ptr).winvis = winvis

window,/free,/pixmap,xsize = xsize,ysize = ysize
(*self.sample_win_ptr).winpix = !d.window
window,/free,/pixmap,xsize = xsize,ysize = ysize
(*self.res_win_ptr).winpix = !d.window
window,/free,/pixmap,xsize = xsize,ysize = ysize
(*self.decon_win_ptr).winpix = !d.window

widget_control,self.tlb,set_uvalue = self

wset,(*self.sample_win_ptr).winpix & erase,self.white
wset,(*self.sample_win_ptr).winvis
device,copy = [0,0,!d.x_size,!d.y_size,0,0,(*self.sample_win_ptr).winpix]

wset,(*self.res_win_ptr).winpix & erase,self.white
wset,(*self.res_win_ptr).winvis
device,copy = [0,0,!d.x_size,!d.y_size,0,0,(*self.res_win_ptr).winpix]

wset,(*self.decon_win_ptr).winpix & erase,self.white
wset,(*self.decon_win_ptr).winvis
device,copy = [0,0,!d.x_size,!d.y_size,0,0,(*self.decon_win_ptr).winpix]

reg_name = 'FT_TOOLKIT'
ev_handler = 'FT_TOOLKIT_EVENT'
cleanup = 'FT_TOOLKIT_CLEANUP'
ret = dave_set_focus(self.tlb)
xmanager,reg_name,self.tlb,event_handler = ev_handler,   $
   cleanup = cleanup,/no_block
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::init_colors
; Initialize some useful colors
self.green = FSC_COLOR("Green", !D.Table_Size-2)
self.white = FSC_COLOR("White", !D.Table_Size-3)
self.black = FSC_COLOR("Black", !D.Table_Size-4)
self.red = FSC_COLOR("Red", !D.Table_Size-5)
self.blue = FSC_COLOR("Blue", !D.Table_Size-6)
tvlct,r,g,b,/get
self.color_ptr = ptr_new({r:r,g:g,b:b})
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit::init, group_leader = group_leader,  $
                           directory = directory,        $
                           notify_ids = notify_ids

ret = self->disclaimer()
if ~ret then return,0

tvlct,r,g,b,/get
self.rptr = ptr_new(r)
self.gptr = ptr_new(g)
self.bptr = ptr_new(b)
device,get_decomposed = dc
self.dc = dc
device,decomposed = 0
ret = self->init_colors()

; Check for input information
if n_elements(group_leader) eq 0 then group_leader = 0L
if n_elements(directory) eq 0 then directory = sourceroot()
if n_elements(notify_ids) eq 0 then notify_ids = [0L,0L]
self.group_leader = group_leader
self.directory = directory
self.notify_ids = notify_ids

; Create and initialize the window structures
self.sample_win_ptr = ptr_new(/allocate_heap)
self.res_win_ptr = ptr_new(/allocate_heap)
self.decon_win_ptr = ptr_new(/allocate_heap)
*self.sample_win_ptr = {ft_win}
*self.res_win_ptr = {ft_win}
*self.decon_win_ptr = {ft_win}
(*self.sample_win_ptr).autoscale = 1B
(*self.res_win_ptr).autoscale = 1B
(*self.decon_win_ptr).autoscale = 1B
(*self.sample_win_ptr).xptr = ptr_new(/allocate_heap)
(*self.res_win_ptr).xptr = ptr_new(/allocate_heap)
(*self.decon_win_ptr).xptr = ptr_new(/allocate_heap)
(*self.sample_win_ptr).yptr = ptr_new(/allocate_heap)
(*self.res_win_ptr).yptr = ptr_new(/allocate_heap)
(*self.decon_win_ptr).yptr = ptr_new(/allocate_heap)

; Build the user-interface
ret = self->build_gui()

return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pro ft_toolkit__define
define = {  ft_toolkit,                   $
            tlb:0L,                       $
            group_leader:0L,              $
            notify_ids:lonarr(2),         $

            color_ptr:ptr_new(),          $

            rptr:ptr_new(),               $
            gptr:ptr_new(),               $
            bptr:ptr_new(),               $

            green:0B,                     $
            white:0B,                     $
            black:0B,                     $
            red:0B,                       $
            blue:0B,                      $

            plot_names:strarr(4),         $
            scale_names:strarr(2),        $
            scat_names:strarr(2),         $

            sample_win_ptr:ptr_new(),     $
            res_win_ptr:ptr_new(),        $
            decon_win_ptr:ptr_new(),      $
            dc:0,                         $

            tab_titles:strarr(3),         $
            res_obj:obj_new(),            $
            sig_obj:obj_new(),            $
            decon_obj:obj_new(),          $
            directory:'',                 $
            sample_slider:0L,             $
            resolution_slider:0L,         $
            decon_slider:0L,              $
            corr_obj:obj_new()            }

end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function ft_toolkit, group_leader = group_leader,     $
                     directory = directory,           $
                     notify_ids = notify_ids

o_ft_toolkit = obj_new('ft_toolkit',   group_leader = group_leader,  $
                                       directory = directory,        $
                                       notify_ids = notify_ids       )
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pro fft_toolkit
o = obj_new('ft_toolkit',directory = 'c:\bhd\dave_demo\browser\')
end