; $Id: db_interface.pro,v 1.72 2007/07/03 18:26:16 azuah Exp $
;###############################################################################
;
; NAME:
;   DB_Interface
;
; PURPOSE:
;   This is the main user interface procedure for the DAVE Data
;   Browser application module. The Data Browser is the main
;   general-purpose visualization module in DAVE with a wide set of
;   capabilties.
;
; CATEGORY:
;   Data Browser
;
; AUTHORS:
;   Richard Tumanjong Azuah
;   azuah@nist.gov; (301) 9755604
;
;   Robert M. Dimeo, Ph.D.
;   Phone: (301) 975-8135
;   E-mail: robert.dimeo@nist.gov
;   http://www.ncnr.nist.gov/staff/dimeo
;
;   NIST Center for Neutron Research
;   100 Bureau Drive
;   Gaithersburg, MD 20899
;   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.
;
;###############################################################################



;===============================================================================
; dave_type
;
; PURPOSE:
;   Determines the dimension of the data contained in the specified
;   DAVE formated data file.
;
; PARAMETERS:
;   filename [in] - the DAVE formated datafile to be examined.
;
; RETURN VALUE:
;   The number of dimensions of the data (dependent data).
;
function dave_type,filename

restore,filename,/RELAXED_STRUCTURE_ASSIGNMENT
qty = (*(*(*davePtr).dataStrPtr).commonStr.histPtr).qty
s = size(qty)
heap_free,davePtr
return,s[0]
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_i_get_filename
;
; PURPOSE:
;   Get just the base file name from a fully qualified name
;
; PARAMETERS:
;   fqpath [in] - fully qualified filename
;
; RETURN VALUE:
;   The base filename without directory info
;
function DB_i_get_filename,fqpath
; Strips off just the file name from a fully qualified path
;length = strlen(fqpath)
;delimiter = path_sep()
;pos = strpos(fqpath,delimiter,/reverse_search)
;if pos ne -1 then filename = strmid(fqpath,pos+1) else $
;	filename = fqpath
;return,filename

; use built-in IDL function
return, file_basename(fqpath)

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



;===============================================================================
; DB_i_create_pretty_plot
;
; PURPOSE:
;   Creates data for a color plot to display in the right panel of the
;   Data Browser interface.
;
; PARAMETERS:
;
; RETURN VALUE:
;   The data to be used for the plot
;
function DB_i_create_pretty_plot

xsize = !d.x_size
ysize = !d.y_size
;print,xsize,ysize
n = ysize
x = FINDGEN(n)
y = COS(x*!PI/6)*EXP(-((x - n/2)/30)^2/10)
; Construct a two-dimensional image of the wave.
zorig = REBIN(y, n, n)
; Add two different rotations to simulate a crystal structure.
z = ROT(zorig, 40) + ROT(zorig, -45)
f = fft(z)
logpower = ALOG10(ABS(f)^2)
return,logpower
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_i_cleanup
;
; PURPOSE:
;   Cleanup procedure called when the tlb for this application is
;   destroyed.
;
; PARAMETERS
;   tlb [in;req] - the widget id that is to be destroyed
;
; KEYWORDS:
;
pro DB_i_cleanup,tlb
widget_control,tlb,get_uvalue = pState
device,decomposed = (*pState).dc

; empty sel_cont
((*pState).sel_cont)->remove, /all

; Delete all containers
; Note that order is important!
; First delete graphic containers BEFORE data containers
objects=[(*pState).o_registry, $
         (*pState).o_1d_gr_cont,(*pState).o_2d_gr_cont, $
         (*pState).o_1d_cont,(*pState).o_2d_cont, $
         (*pState).o_active_gr1D_cont,(*pState).o_active_gr2DC_cont, $
         (*pState).o_active_gr2DI_cont,(*pState).o_active_gr2DS_cont, $
         (*pState).sel_cont,(*pState).pan_container]
obj_destroy,objects
ptr_free,(*pState).cur_sel_ptr
tvlct,(*pState).r,(*pState).g,(*pState).b
ptr_free,pState

if (!debug) then begin
    ;; Display the heap memory to see if there are any leaks
    help,/heap,/brief
endif

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



;===============================================================================
; DB_i_load
;
; PURPOSE:
;   Event handler. Loads DAVE data from file into an appropriate
;   DaveDat* class (1-d or 2-d) and put them in an appropriate data
;   container.
;
; PARAMETERS
;   event [in] - the event structure.
;
; KEYWORDS:
;
pro DB_i_load,event
widget_control,event.top,get_uvalue = pState
; User selects a file
filename = dialog_pickfile(dialog_parent = event.top,$
                           title = 'Select DAVE data file to load',$
                           /read,filter = '*.dave',$
                           path = (*pState).data_directory, $
                           get_path = this_path,/multiple_files)
if filename[0] eq '' then return
; Pull out the file name from the fully-qualified path for displaying
; on the leaf.

nfiles = n_elements(filename)
for i = 0,nfiles-1 do begin

    display_name = strmid(filename[i],strlen(this_path))

    subs = '.DAVE'
    len = strpos(strupcase(display_name),subs)
    if (len gt 0) then display_name = strmid(display_name,0,len) ; strip off .dave extension from nametag

    type = dave_type(filename[i])
    ;; Create the proper DAVE Data Object from the DAVE Pointer depending
    ;; on the type of data it contains.  Put the object into the
    ;; appropriate container object.
    if type eq 1 then begin	; 1-d data
        o1 = obj_new('davedat1d',filename=filename[i], nameTag=display_name)
        ;;oGr1 = obj_new('daveGr1d',dataobj = o1)
        ;;obj_destroy,o1
        (*pState).o_1d_cont->add,o1
        ;; Add a leaf
        uname = 'ONED_DATA'
        id = widget_info(event.top,find_by_uname=uname)
        new_leaf = widget_tree(id,value=display_name,uvalue=o1,uname='SP_ONED_DATA',bitmap=(*pState).dataBMP)
    endif else begin            ; 2-d data
        o2 = obj_new('davedat2d',filename=filename[i],nameTag=display_name)
        ;;oGr2 = obj_new('daveGr2d',dataobj = o2)
        ;;obj_destroy,o2
        (*pState).o_2d_cont->add,o2
        ;; Add a leaf
        uname = 'TWOD_DATA'
        id = widget_info(event.top,find_by_uname = uname)
        new_leaf = widget_tree(id,value=display_name,uvalue=o2,uname='SP_TWOD_DATA',bitmap=(*pState).dataBMP)
    endelse
endfor

; update data_directory to directory where files were just loaded
(*pState).data_directory=this_path

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



;===============================================================================
; DB_i_remove
;
; PURPOSE:
;   Event handler. Removes the selected data from the Data Browser.
;
; PARAMETERS
;   event [in] - the event structure.
;
; KEYWORDS:
;
pro DB_i_remove,event
widget_control,event.top,get_uvalue = pState
tree_id = widget_info(event.top,find_by_uname = 'TREE')
leaf_ids = widget_info(tree_id,/tree_select)
if (not widget_info(leaf_ids[0],/valid_id)) then return
parent_name = widget_info(widget_info(leaf_ids[0],/parent),/uname)
VALID_PARENTS = ['ONED_DATA','TWOD_DATA','ONED_PLOT','TWOD_PLOT']
index = where(VALID_PARENTS eq parent_name, parent_isValid)
index = index[0]
if (parent_isValid) then begin
    n_leaves = n_elements(leaf_ids)
    for i = 0,n_leaves-1 do begin
        parent = widget_info(leaf_ids[i],/parent)
        uname = widget_info(parent,/uname)
        ;;if uname eq 'ONED_DATA' then cont = (*pState).o_1d_cont else $
        ;;	cont = (*pState).o_2d_cont
        case index of
            '0': oCont = (*pState).o_1d_cont
            '1': oCont = (*pState).o_2d_cont
            '2': oCont = (*pState).o_1d_gr_cont
            '3': oCont = (*pState).o_2d_gr_cont
        endcase
        widget_control,leaf_ids[i],get_uvalue = object
        if ((index eq 0) or (index eq 1)) then begin
            void = object->get(nReferences=nReferences)
            if (nReferences ne 0) then begin
                title = 'DATABROWSER - Remove Data'
                message = ['Operation not allowed!']
                message = [message,'Attempt to delete data that is in use.']
                message = [message,'Please delete all plots which use this data first']
                void = dialog_message(message,/information,title=title,dialog_parent=event.top)
                return
            endif
        endif
        if obj_valid(object) then begin
            oCont->remove,object
            if (index eq 2 || index eq 3) then begin
                if (object->get(visRef=visRef) && obj_valid(visRef)) then begin
                    visTLB = visRef->getTLB()
                    widget_control, visTLB, /destroy
                endif
            endif 
            obj_destroy,object
            widget_control,leaf_ids[i],/destroy
        endif
    endfor
    (*pState).cur_id = 0L
endif
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_i_context
;
; PURPOSE:
;   Event handler. Display context-sensitive pop-up menus at the
;   current mouse position.
;
; PARAMETERS
;   event [in] - the event structure.
;
; KEYWORDS:
;
pro DB_i_context,event
widget_control,event.top,get_uvalue = pState
; First determine which context menu to display: 1-d or 2-d
tree_id = widget_info(event.top,find_by_uname = 'TREE')
leaf_ids = widget_info(tree_id,/tree_select)
; If no leaves are selected then get out
if not widget_info(leaf_ids[0],/valid_id) then return
; Ok, we've got some valid leaves selected
n_leaves = n_elements(leaf_ids)
parent_name = widget_info(widget_info(leaf_ids[0],/parent),/uname)
VALID_PARENTS = ['ONED_DATA','TWOD_DATA','ONED_PLOT','TWOD_PLOT']
void = where(VALID_PARENTS eq parent_name, parent_isValid)
if (parent_isValid) then begin
    ;; Test if all of the selected leaves are of the same type
    for i = 0,n_leaves-1 do begin
        widget_control,leaf_ids[i],get_uvalue = object
        if (not obj_valid(object)) then return
        class = obj_class(object)
        if i eq 0 then class_array = [class] else class_array = [class,class_array]
    endfor
endif else return
index = where(class_array eq class_array[0],count_same)
; If the leaves are NOT all of the same type then get out!
if count_same ne n_leaves then return
; Choose the proper context-sensitive menu!
case class_array[0] of
    'DAVEDAT1D': context_base = widget_info(event.top,find_by_uname = 'ONED_CONTEXT_BASE')
    'DAVEDAT2D': context_base = widget_info(event.top,find_by_uname = 'TWOD_CONTEXT_BASE')
    'DAVEGR1D': context_base = widget_info(event.top,find_by_uname = 'ONEDGR_CONTEXT_BASE')
    'DAVEGR2D': context_base = widget_info(event.top,find_by_uname = 'TWODGR_CONTEXT_BASE')
endcase

; Now display the appropriate context-sensitive menu
widget_displaycontextmenu, event.id, event.x, event.y, context_base
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_i_resetData
;
; PURPOSE:
;   Revert the data in the selected objects to their prestine copy as
;   stored in their davePtr class variable .
;
; PARAMETERS
;   event [in] - the event structure.
;
; KEYWORDS:
;
pro DB_i_resetData, event

widget_control,event.top,get_uvalue = pState

title = 'DATABROWSER - Revert Data'
dataRef = ((*pState).sel_cont)->get(/all, count=nData)
while (nData gt 0) do begin
    nData = nData - 1
    if (not (dataRef[nData])->resetData()) then begin
        void = dialog_message(!error_state.msg,/error,title=title,dialog_parent=event.top)
        continue
    endif
endwhile

(*pState).o_registry->refresh

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



;===============================================================================
; DB_i_transpose
;
; PURPOSE:
;   Event handler. Transpose the data in the selected objects.
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_transpose, event

widget_control,event.top,get_uvalue = pState

dataRef = ((*pState).sel_cont)->get(/all, count=nData)
while (nData gt 0) do begin
    nData = nData - 1
    if (~obj_isa(dataRef[nData],'DAVEDAT2D')) then continue ; has to be 2D
    if (not (dataRef[nData])->transpose()) then begin
        void = dialog_message(!error_state.msg,/error,title=title,dialog_parent=event.top)
        continue
    endif
endwhile

(*pState).o_registry->refresh, /transpose

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



;===============================================================================
; DB_i_cloneData
;
; PURPOSE:
;   Event handler. Clone the data in the selected objects.
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_cloneData, event

widget_control,event.top,get_uvalue = pState

title = 'DATABROWSER - Clone Data'

; Get the selected data
dataRef = ((*pState).sel_cont)->get(/all, count=nData)

; Get the widget ID's for the 1,2D data branches of the tree widget
onedBranchID = widget_info(event.top,find_by_uname='ONED_DATA')
twodBranchID = widget_info(event.top,find_by_uname='TWOD_DATA')

; Loop through selected data, clone and add them to the appropriate
; branch of the tree widget
while (nData gt 0) do begin
    nData = nData - 1
    newObj = (dataRef[nData])->clone()
    if (not obj_valid(newObj)) then begin
        void = dialog_message(!error_state.msg,/information,title=title,dialog_parent=event.top)
        continue
    endif
    void = (dataRef[nData])->get(nameTag=nameTag)
    nameTag = 'Copy of '+nameTag
    void = newObj->set(nameTag=nameTag)
    case obj_class(newObj) of
        'DAVEDAT1D': begin
            new_leaf = widget_tree(onedBranchID,value=nameTag,uvalue=newObj,uname='SP_ONED_DATA',bitmap=(*pState).dataBMP)
            (*pState).o_1d_cont->add,newObj
        end
        'DAVEDAT2D': begin
            new_leaf = widget_tree(twodBranchID,value=nameTag,uvalue=newObj,uname='SP_TWOD_DATA',bitmap=(*pState).dataBMP)
            (*pState).o_2d_cont->add,newObj
        end
        else: obj_destroy, newObj
    endcase
endwhile

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



;===============================================================================
; DB_i_extractSpectra
;
; PURPOSE:
;   Extract 'spectra' from a DaveDat2D object. Create DaveDat1D
;   objects from the extracted spectra.
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro  DB_i_extractSpectra, event

widget_control,event.top,get_uvalue = pState

; Launch a dialog to obtain details of the spectra to be extracted.
dataRef = ((*pState).sel_cont)->get(position=0)
returnStr = db_xSpectra(dataRef,group_leader=event.top)
if (returnStr.cancel) then return ; dialog was cancelled

axis = (returnStr.axis eq 0)? 'x' : 'y'
spectra = returnStr.spectra
if (spectra[0] eq -1) then return ; no spectra were entered

title = 'DATABROWSER - Extract Spectra'
for i=0, n_elements(spectra) - 1 do begin
    newObj = dataRef->xspec(axis,spectra[i]-1) ; convert spectrum to a zero based index value
    if (obj_valid(newObj) ne 1) then begin
        void = dialog_message(!error_state.msg,/information,title=title,dialog_parent=event.top)
        continue
    endif
    ;; Add new object to the 1-D container
    (*pState).o_1d_cont->add,newObj
    ;; Add a new leaf in '1-D Data' branch
    void = newObj->get(nameTag = display_name)
    id = widget_info(event.top,find_by_uname ='ONED_DATA')
    new_leaf = widget_tree(id,value=display_name,uvalue=newObj,uname='SP_ONED_DATA',bitmap=(*pState).dataBMP)
endfor

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



;===============================================================================
; DB_i_addToSelCont
;
; PURPOSE:
;   Register items that are currently selected from the tree
;   widgets. Modify program behaviour accordingly based on selections.
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_addToSelCont,event

widget_control,event.top,get_uvalue = pState
tree_id = widget_info(event.top,find_by_uname = 'TREE')
leaf_ids = widget_info(tree_id,/tree_select)
; Empty the selected container
(*pState).sel_cont->remove,/all
; If no leaves are selected then get out
if not widget_info(leaf_ids[0],/valid_id) then return
; Ok, we've got some valid leaves selected
n_leaves = n_elements(leaf_ids)
if not widget_info(leaf_ids[0],/valid_id) then return
for i = 0,n_leaves-1 do begin
	widget_control,leaf_ids[i],get_uvalue = object
	; Is this a valid object? (i.e. is there one present?)  One possibility
	; is that we've selected a branch, not a leaf.  If it is a branch then
	; there will be no object.  This is the situation handled in the "ELSE"
	; part of the following IF-THEN-ELSE statement.
	if obj_valid(object) then begin
		(*pState).sel_cont->add,object
	endif else begin
	; Nothing valid here so empty the container object, clear the cur_id, and
	; get out.
		(*pState).sel_cont->remove,/all
		(*pState).cur_id = 0L
		return
	endelse
endfor
; Store the (one) widget id for the leaf just selected.  Even though multiple
; leaves may have been selected, only one triggers this event handler.
(*pState).cur_id = event.id

; Some menu items should only be available for one OR multiple data
; selections BUT not BOTH. Define these here.
id1 = lindgen(14) ; <-- only apply to single datasets
id1[0] = widget_info(event.top,find_by_uname = 'CUT')
id1[1] = widget_info(event.top,find_by_uname = 'CHANGE_NTAG_ONED')
id1[2] = widget_info(event.top,find_by_uname = 'CHANGE_NTAG_TWOD')
id1[3] = widget_info(event.top,find_by_uname = 'DSCPTR_ONED')
id1[4] = widget_info(event.top,find_by_uname = 'DSCPTR_TWOD')
id1[5] = widget_info(event.top,find_by_uname = 'LAUNCH_PAN_ONED')
id1[6] = widget_info(event.top,find_by_uname = 'LAUNCH_PAN_TWOD')
id1[7] = widget_info(event.top,find_by_uname = 'DISPHIST_ONED')
id1[8] = widget_info(event.top,find_by_uname = 'DISPHIST_TWOD')
id1[9] = widget_info(event.top,find_by_uname='EXTRACT_SPEC')
id1[10] = widget_info(event.top,find_by_uname = 'SAVE_ONED_DAVE')
id1[11] = widget_info(event.top,find_by_uname = 'SAVE_TWOD_DAVE')
id1[12] = widget_info(event.top,find_by_uname = 'DISPDATA_ONED')
id1[13] = widget_info(event.top,find_by_uname = 'DISPDATA_TWOD')
n1 = n_elements(id1)

id2 = lindgen(2) ; <-- only apply when multiple datasets selected
id2[0] = widget_info(event.top,find_by_uname = 'ZIP_ONED')
id2[1] = widget_info(event.top,find_by_uname = 'ZIP_TWOD')
n2 = n_elements(id2)

if n_leaves eq 1 then begin
    for i=0,n1-1 do widget_control,id1[i],sensitive=1
    for i=0,n2-1 do widget_control,id2[i],sensitive=0
endif else begin
    for i=0,n1-1 do widget_control,id1[i],sensitive=0
    for i=0,n2-1 do widget_control,id2[i],sensitive=1
endelse
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_i_rebin
;
; PURPOSE:
;   Rebin selected data
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_rebin,event

widget_control,event.top,get_uvalue = pState
; Launch the widget rebinning interface
s_out = db_xrebin(group_leader = event.top,(*pState).sel_cont)
if s_out.cancel then return
; Cycle through each class in the sel_cont object, determine if it is one-d or
; two-d and then rebin appropriately.
n_obj = (*pState).sel_cont->count()
oall = (*pState).sel_cont->get(/all)

for i = 0,n_obj-1 do begin
	class = obj_class(oall[i])
        iaxis = (s_out.bin_axis[i] eq 'x')? 0 : 1
	lo_lim = s_out.bin_min[iaxis,i]
	hi_lim = s_out.bin_max[iaxis,i]
	width = s_out.bin_width[iaxis,i]
	nt = s_out.no_truncate
        ;; Get the "DAVEDAT" object out
        if class eq "DAVEDAT1D" then begin
            result = (oall[i])->rebin(lo_lim,hi_lim,width,notruncate = nt)
        endif else begin
            result = (oall[i])->rebin(s_out.bin_axis[i],lo_lim,hi_lim,width,notruncate = nt)
	endelse
endfor

(*pState).o_registry->refresh
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_i_cut
;
; PURPOSE:
;   Cut selected data
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_cut,event

widget_control,event.top,get_uvalue = pState
; Launch the data cutting interface
s_out = db_dataCuts(group_leader = event.top,(*pState).sel_cont)
;return	; for debugging

if s_out.cancel then return
nobj = (*pState).sel_cont->count()
; Get all of the DAVEGR2D object references out
oall = (*pState).sel_cont->get(/all)
; Which file is the output file?
ntag_array = strarr(nobj)
for i = 0,nobj-1 do begin
    result = oall[i]->get(nameTag=nameTag)
    ntag_array[i] = nameTag
endfor

index = where(ntag_array eq s_out.fileout)
o = oall[index[0]]

; How many cuts?
ncuts = n_elements(s_out.center)
for i = 0,ncuts-1 do begin
	center = s_out.center[i]
	width = s_out.width[i]
	cutaxis = s_out.cutaxis[i]
        lo_lim = center-0.5*width
	up_lim = center+0.5*width

	;; Get the "DAVEDAT" object out
        result = o->get(nameTag = nameTag)
        new_object = o->cut(cutaxis, lo_lim, up_lim)
	;; Add new object to the 1-d container
        (*pState).o_1d_cont->add,new_object
	;; Add a new leaf
	uname = 'ONED_DATA'
        id = widget_info(event.top,find_by_uname = uname)
        display_name = 'cut_'+strtrim(string(i),2)+'_'+DB_i_get_filename(nameTag)
        void = new_object->set(nameTag=display_name)
        new_leaf = widget_tree(id,value = display_name,uvalue = new_object, $
                               uname = 'SP_ONED_DATA',bitmap=(*pState).dataBMP)
endfor

(*pState).o_registry->refresh
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_i_change_name
;
; PURPOSE:
;   Alter data/plot label in tree widget
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_change_name,event

widget_control,event.top,get_uvalue = pState
tree_id = widget_info(event.top,find_by_uname = 'TREE')
leaf_ids = widget_info(tree_id,/tree_select)
if not widget_info(leaf_ids[0],/valid_id) then return
widget_control,leaf_ids[0],get_value = name, get_uvalue=objRef
name = name[0]
name_structure = db_get_new_name(group_leader = event.top,name)
if name_structure.cancel then return
widget_control,leaf_ids[0],set_value = name_structure.name
void = objRef->set(nameTag=name_structure.name)

(*pState).o_registry->refresh

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



;===============================================================================
; DB_i_save2ASCII3COL
;
; PURPOSE:
;   Save selected dataset to an ASCII 3 Column formated file.
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_save2ASCII3COL, event

widget_control, event.top,get_uvalue=pState

; Get the selected object
dataRef = ((*pState).sel_cont)->get(position=0)

; the object has to be a DAVEDAT* object
valid_types=['DAVEDAT1D']
res = where((valid_types eq vartype(dataRef)), Valid)
if (not Valid) then return

; Get the data from the dataset
if (not (dataRef[0])->get(xVal=xVal,zVal=zVal,zErr=zErr,nametag=def_ofile)) then begin
    title = 'DATABROWSER - Save ASCII 3 Columns'
    void = dialog_message(!error_state.msg,/error,title=title,dialog_parent=event.top)
    return
endif

; Display a dialog to obtain filename to save to
def_ofile = DB_i_get_filename(def_ofile) ; base filename
; replace extension, if any
pos = strpos(def_ofile,'.',/reverse_search)
if (pos ne -1) then def_ofile = strmid(def_ofile,0,pos)
def_ofile += '.txt'

repeat begin
    okay = 1
    ofile = dialog_pickfile(dialog_parent=event.top, $
                            title="Select output filename", $
                            path=(*pState).data_directory, $
                            file=def_ofile, $
                            get_path=newPath, $
                            filter=['*.txt','*.dat'] $
                           )

    if (strtrim(ofile,2) eq '') then begin ; dialog_pickfile was cancelled so do nothing further
        return
    endif

    if ( strmid(ofile,strlen(ofile)-1,1) eq !delimiter) then $ ; ie only a directory was return...
      ofile = ofile+def_ofile   ; then append def_ofile to it

    ;; if file exists then make sure user wishes to overide it
    void = file_search(ofile,count=cnt)
    if (cnt) then begin
        message = [ofile+' exists','Do you want to overwrite it?']
        ans = dialog_message(message,dialog_parent=event.top,/question)
        okay = (strtrim(ans,2) eq 'Yes')? 1 : 0
    endif
endrep until okay

if (strlen(strtrim(newPath,2)) gt 1) then $
  (*pState).data_directory = newPath ; update current working directory

; write to disk and inform user of ouput destination
openw, lun, ofile, /get_lun, error=errFlag ; open output file
if (errFlag ne 0) then begin    ; if error, print the error msg and exit
    title = 'DATABROWSER - Save ASCII 3 Columns'
    void = dialog_message(!error_state.msg,/error,title=title,dialog_parent=event.top)
    return
endif

;; write to disk and inform user of ouput destination
fmt = '(3(G11.4,2X))'
for i = 0,n_elements(xVal)-1 do printf, lun, format=fmt,xVal[i],zVal[i],zErr[i]

free_lun, lun, /force

message=['Output saved in:',ofile]
void = dialog_message(message,dialog_parent=event.top,/information)

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



;===============================================================================
; DB_i_save2ASCIIGRP
;
; PURPOSE:
;   Save selected dataset to an ASCII group formated file.
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_save2ASCIIGRP, event

widget_control, event.top,get_uvalue=pState

; Get the selected object
dataRef = ((*pState).sel_cont)->get(position=0)

; the object has to be a DAVEDAT* object
valid_types=['DAVEDAT2D']
res = where((valid_types eq vartype(dataRef)), Valid)
if (not Valid) then return

title = 'DATABROWSER - Save ASCII Group'

if (~dataRef->get(xval=xData,xLabel=xlabel, xType=xtype, xUnits=xunits $
             ,yVal=yData, yLabel=ylabel, yType=ytype, yUnits=yunits $
             ,zVal=qty, zErr=err, zLabel=qtyLabel, zUnits=qtyUnits $
             ,treatment=treatment,nameTag=def_ofile $
            )) then begin
    void = dialog_message(!error_state.msg,/error,title=title,dialog_parent=event.top)
    return
endif
errPresent = 1

; Display a dialog to obtain filename to save to
def_ofile = DB_i_get_filename(def_ofile) ; base filename
; replace extension, if any
pos = strpos(def_ofile,'.',/reverse_search)
if (pos ne -1) then def_ofile = strmid(def_ofile,0,pos)
def_ofile += '.txt'

repeat begin
    okay = 1
    ofile = dialog_pickfile(dialog_parent=event.top, $
                            title="Select output filename", $
                            path=(*pState).data_directory, $
                            file=def_ofile, $
                            get_path=newPath, $
                            filter=['*.txt','*.grp'] $
                           )

    if (strtrim(ofile,2) eq '') then begin ; dialog_pickfile was cancelled so do nothing further
        return
    endif

    if ( strmid(ofile,strlen(ofile)-1,1) eq !delimiter) then $ ; ie only a directory was return...
      ofile = ofile+def_ofile   ; then append def_ofile to it

    ;; if file exists then make sure user wishes to overide it
    void = file_search(ofile,count=cnt)
    if (cnt) then begin
        message = [ofile+' exists','Do you want to overwrite it?']
        ans = dialog_message(message,dialog_parent=event.top,/question)
        okay = (strtrim(ans,2) eq 'Yes')? 1 : 0
    endif
endrep until okay

if (strlen(strtrim(newPath,2)) gt 1) then $
  (*pState).data_directory = newPath ; update current working directory

nx = n_elements(xData)
ny = n_elements(yData)
nqty1 = (size(qty))[1]
nqty2 = (size(qty))[2]
if (nx ne nqty1) then begin
    isHistogram = strcmp(xType,'HISTOGRAM',4,/FOLD_CASE)
    if (isHistogram && (nx eq nqty1+1)) then begin
        ;; convert to point mode. ASCII grp format only stores point
        ;; mode data
        index = lindgen(nqty1)
        xData = 0.5*(xData[index]+xData[index+1]) ; ==> mid values of the bin boundaries
        nx = n_elements(xData)
    endif else begin
        msg = 'Size of dependent and first independent data do not match!'
        void = dialog_message(msg,/error,title=title,dialog_parent=event.top)
        return
    endelse
endif
if (ny ne nqty2) then begin
    isHistogram = strcmp(yType,'HISTOGRAM',4,/FOLD_CASE)
    if (isHistogram && (ny eq nqty2+1)) then begin
        ;; convert to point mode
        index = lindgen(nqty2)
        yData = 0.5*(yData[index]+yData[index+1]) ; ==> mid values of the bin boundaries
        ny = n_elements(yData)
    endif else begin
        msg = 'Size of dependent and second independent data do not match!'
        void = dialog_message(msg,/error,title=title,dialog_parent=event.top)
        return
    endelse
endif
if (~errPresent || (n_elements(qty) ne n_elements(err))) then begin
    err = qty*0.0 + 1.0         ; force error to 1.0 if necessary
endif

;; open output file
openw, lun, ofile, /get_lun, error=errFlag
if (errFlag ne 0) then begin    ; if error, get the error msg and exit
    void = dialog_message(msg,/error,title=title,dialog_parent=event.top)
    free_lun, lun, /force
    return
endif   

;; write to disk and inform user of ouput destination
;; the nos of x values
printf, lun, '# Number of x values ('+xLabel+')'
printf, lun, strtrim(string(n_elements(xData)),2)
;; the nos of y values
printf, lun, '# Number of y values ('+yLabel+')'
printf, lun, strtrim(string(n_elements(yData)),2)
;; The x values
printf, lun, '# x ('+xLabel+')'
for i = 0,nx-1 do printf, lun, format='(G11.4)',xdata[i]
;; The y values
printf, lun, '# y ('+yLabel+')'
for i = 0,ny-1 do printf, lun, format='(G11.4)',ydata[i]
;; The data values
for i=0,ny-1 do begin
    printf, lun, '# Group ',strtrim(string(i+1),2),' of ',strtrim(string(ny),2)
    for j=0,nx-1 do printf, lun, format='(2(G11.4,2X))', qty[j,i], err[j,i]
endfor

free_lun, lun, /force

message=['Output saved in:',ofile]
void = dialog_message(message,dialog_parent=event.top,/information)

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



;===============================================================================
; DB_i_save2ASCIISPE
;
; PURPOSE:
;   Save selected dataset to an ASCII SPE formated file.
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_save2ASCIISPE, event

widget_control, event.top,get_uvalue=pState

; Get the selected object
dataRef = ((*pState).sel_cont)->get(position=0)

; the object has to be a DAVEDAT* object
valid_types=['DAVEDAT2D']
res = where((valid_types eq vartype(dataRef)), Valid)
if (not Valid) then return

title = 'DATABROWSER - Save ASCII Group'

if (~dataRef->get(xval=xData,xLabel=xlabel, xType=xtype, xUnits=xunits $
             ,yVal=yData, yLabel=ylabel, yType=ytype, yUnits=yunits $
             ,zVal=qty, zErr=err, zLabel=qtyLabel, zUnits=qtyUnits $
             ,treatment=treatment,nameTag=def_ofile $
            )) then begin
    void = dialog_message(!error_state.msg,/error,title=title,dialog_parent=event.top)
    return
endif
if (qtyLabel eq '') then qyLabel = 'Data'
errPresent = 1

; Display a dialog to obtain filename to save to
def_ofile = DB_i_get_filename(def_ofile) ; base filename
; replace extension, if any
pos = strpos(def_ofile,'.',/reverse_search)
if (pos ne -1) then def_ofile = strmid(def_ofile,0,pos)
def_ofile += '.spe'

repeat begin
    okay = 1
    ofile = dialog_pickfile(dialog_parent=event.top, $
                            title="Select output filename", $
                            path=(*pState).data_directory, $
                            file=def_ofile, $
                            get_path=newPath, $
                            filter=['*.spe'] $
                           )

    if (strtrim(ofile,2) eq '') then begin ; dialog_pickfile was cancelled so do nothing further
        return
    endif

    if ( strmid(ofile,strlen(ofile)-1,1) eq !delimiter) then $ ; ie only a directory was return...
      ofile = ofile+def_ofile   ; then append def_ofile to it

    ;; if file exists then make sure user wishes to overide it
    void = file_search(ofile,count=cnt)
    if (cnt) then begin
        message = [ofile+' exists','Do you want to overwrite it?']
        ans = dialog_message(message,dialog_parent=event.top,/question)
        okay = (strtrim(ans,2) eq 'Yes')? 1 : 0
    endif
endrep until okay

if (strlen(strtrim(newPath,2)) gt 1) then $
  (*pState).data_directory = newPath ; update current working directory

nx = n_elements(xData)
ny = n_elements(yData)
nqty1 = (size(qty))[1]
nqty2 = (size(qty))[2]
if (nx ne nqty1) then begin
    isHistogram = strcmp(xType,'HISTOGRAM',4,/FOLD_CASE)
    if (isHistogram && (nx eq nqty1+1)) then begin
        ;; convert to point mode. ASCII grp format only stores point
        ;; mode data
        index = lindgen(nqty1)
        xData = 0.5*(xData[index]+xData[index+1]) ; ==> mid values of the bin boundaries
        nx = n_elements(xData)
    endif else begin
        msg = 'Size of dependent and first independent data do not match!'
        void = dialog_message(msg,/error,title=title,dialog_parent=event.top)
        return
    endelse
endif
if (ny ne nqty2) then begin
    isHistogram = strcmp(yType,'HISTOGRAM',4,/FOLD_CASE)
    if (isHistogram && (ny eq nqty2+1)) then begin
        ;; convert to point mode
        index = lindgen(nqty2)
        yData = 0.5*(yData[index]+yData[index+1]) ; ==> mid values of the bin boundaries
        ny = n_elements(yData)
    endif else begin
        msg = 'Size of dependent and second independent data do not match!'
        void = dialog_message(msg,/error,title=title,dialog_parent=event.top)
        return
    endelse
endif
if (~errPresent || (n_elements(qty) ne n_elements(err))) then begin
    err = qty*0.0 + 1.0         ; force error to 1.0 if necessary
endif

;; open output file
openw, lun, ofile, /get_lun, error=errFlag
if (errFlag ne 0) then begin    ; if error, get the error msg and exit
    void = dialog_message(msg,/error,title=title,dialog_parent=event.top)
    free_lun, lun, /force
    return
endif   

;; write to disk and inform user of ouput destination
printf, lun, format='(2I5)',nqty2,nqty1
;; The y values
printf, lun, '### '+yLabel
printf, lun, format='(8(G10.4))',yData
;; The x values
printf, lun, '### '+xLabel
printf, lun, format='(8(G10.4))',xData
;; The data values
for i=0,nqty2-1 do begin
    printf, lun, '### '+qtyLabel
    printf, lun, format='(8(G10.4))', qty[*,i]
    printf, lun, '### Errors'
    printf, lun, format='(8(G10.4))', err[*,i]
endfor

free_lun, lun, /force

message=['Output saved in:',ofile]
void = dialog_message(message,dialog_parent=event.top,/information)

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



;===============================================================================
; DB_i_save2Dave
;
; PURPOSE:
;   Save selected dataset to a DAVE formated file.
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_save2Dave, event

widget_control, event.top,get_uvalue=pState

; Get the selected object
dataRef = ((*pState).sel_cont)->get(position=0)

; the object has to be a DAVEDAT* object
valid_types=['DAVEDAT1D','DAVEDAT2D']
res = where((valid_types eq vartype(dataRef)), Valid)
if (not Valid) then return

; Get the datasets davePtr (the updated version)
if (not (dataRef[0])->get(updDavePtr=davePtr,nameTag=def_ofile)) then begin
    heap_free, davePtr
    title = 'DATABROWSER - Save'
    void = dialog_message(!error_state.msg,/error,title=title,dialog_parent=event.top)
    return
endif

; Display a dialog to obtain filename to save to
def_ofile = DB_i_get_filename(def_ofile) ; base filename
; replace extension, if any
pos = strpos(def_ofile,'.',/reverse_search)
if (pos ne -1) then def_ofile = strmid(def_ofile,0,pos)
def_ofile += '.dave'


repeat begin
    okay = 1
    ofile = dialog_pickfile(dialog_parent=event.top, $
                            title="Select output filename", $
                            path=(*pState).data_directory, $
                            file=def_ofile, $
                            get_path=newPath, $
                            filter='*.dave' $
                           )

    if (strtrim(ofile,2) eq '') then begin ; dialog_pickfile was cancelled so do nothing further
        heap_free, davePtr
        return
    endif

    if ( strmid(ofile,strlen(ofile)-1,1) eq !delimiter) then $ ; ie only a directory was return...
      ofile = ofile+def_ofile   ; then append def_ofile to it

    ;; if file exists then make sure user wishes to overide it
    void = file_search(ofile,count=cnt)
    if (cnt) then begin
        message = [ofile+' exists','Do you want to overwrite it?']
        ans = dialog_message(message,dialog_parent=event.top,/question)
        okay = (strtrim(ans,2) eq 'Yes')? 1 : 0
    endif
endrep until okay

if (strlen(strtrim(newPath,2)) gt 1) then $
  (*pState).data_directory = newPath ; update current working directory

; write to disk and inform user of ouput destination
save, davePtr, filename=ofile
message=['Output saved in:',ofile]
void = dialog_message(message,dialog_parent=event.top,/information)

; final cleanup
heap_free, davePtr

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



;===============================================================================
; DB_i_editDescriPtr
;
; PURPOSE:
;   Edit/view descriPtr information tag for selected data
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_editDescriPtr, event

widget_control, event.top,get_uvalue=pState

; Get the selected object
dataRef = ((*pState).sel_cont)->get(position=0)

; the object has to be a DAVEDAT* object
valid_types=['DAVEDAT1D','DAVEDAT2D']
res = where((valid_types eq vartype(dataRef)), Valid)
if (not Valid) then return

; get the descriPtr
void = dataRef->get(descriPtr=dscPtr)

; display dialog to view/edit the descriPtr
res = db_editdescriptr(dscPtr,group_leader=event.top)

if (res) then begin
    void = dataRef->set(descriPtr=dscPtr)
endif

if (ptr_valid(dscPtr)) then ptr_free, dscPtr

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



;===============================================================================
; DB_i_dispHist
;
; PURPOSE:
;   View treatment information for selected data
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_dispHist, event

widget_control, event.top,get_uvalue=pState

; Get the selected object
dataRef = ((*pState).sel_cont)->get(position=0)

; the object has to be a DAVEDAT* object
valid_types=['DAVEDAT1D','DAVEDAT2D']
res = where((valid_types eq vartype(dataRef)), Valid)
if (not Valid) then return

; get the tratment info
void = dataRef->get(treatment=treatment)

; display the treatment info
db_disphist, treatment, group_leader=event.top

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



;===============================================================================
; DB_i_dispData
;
; PURPOSE:
;   View the (independent and dependent) data for selected dataset
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_dispData, event

widget_control, event.top,get_uvalue=pState

; Get the selected object
dataRef = ((*pState).sel_cont)->get(position=0)

; the object has to be a DAVEDAT* object
valid_types=['DAVEDAT1D','DAVEDAT2D']
res = where((valid_types eq vartype(dataRef)), Valid)
if (not Valid) then return

; get the davePtr and filename
void = dataRef->get(updDavePtr=davePtr,filename=filename)

; display the treatment info
db_DispData, davePtr,filename, group_leader=event.top

heap_free,davePtr

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



;===============================================================================
; DB_i_offset
;
; PURPOSE:
;   Apply an offset to selected dataset
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_offset,event
widget_control,event.top,get_uvalue = pState
offStr = db_offset(group_leader = event.top, $
                          (*pState).sel_cont)
if offStr.cancel then return
oall = (*pState).sel_cont->get(/all,count=n_obj)
for i = 0,n_obj-1 do begin
    ;; Get the "DAVEDAT" object out
    ;;result = oall[i]->get(dataref = o)
    o = oall[i]
    ;; Set the offset
    result = o->set(offset = offStr.offset[i])
endfor

(*pState).o_registry->refresh

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



;===============================================================================
; DB_i_sfactor
;
; PURPOSE:
;   Apply a multiplicative scalefactor to selected dataset
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_sfactor,event

widget_control,event.top,get_uvalue = pState
sfStr = db_scalefactor(group_leader = event.top, $
                       (*pState).sel_cont)
if sfStr.cancel then return
oall = (*pState).sel_cont->get(/all,count=n_obj)
for i = 0,n_obj-1 do begin
    ;; Get the "DAVEDAT" object out
    ;result = oall[i]->get(dataref = o)
    o = oall[i]
    ; Set the offset
    result = o->set(scalefactor = sfStr.scalefactor[i])
endfor

(*pState).o_registry->refresh

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



;===============================================================================
; DB_i_pan
;
; PURPOSE:
;   Send selected dataset to PAN module for analysis.
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_pan,event
widget_control,event.top,get_uvalue = pState
; Get a copy of the davePtr
tree_id = widget_info(event.top,find_by_uname = 'TREE')
leaf_ids = widget_info(tree_id,/tree_select)
; Get the data object out of the DAVEGR1D or DAVEGR2D object
widget_control,leaf_ids[0],get_uvalue = oDat
;result = oGr->get(dataref = oDat)

; get the davePtr using the 'updDavePtr' keyword which updates the
; objects original davePtr with its more current data.
result = oDat->get(updDavePtr = davePtr)

file_path = (*pState).data_directory
o = obj_new('opan',	group_leader = event.top,	$
					davePtr = davePtr,			$
					workDir = file_path,		$
					dataDir = file_path, 		$
					ob_reg = (*pState).o_registry)
heap_free,davePtr
(*pState).pan_container->add,o

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



;===============================================================================
; DB_i_zipper
;
; PURPOSE:
;   Create a single 2D dataset from several 1D datasets by merging
;   them. The descriPtr information for each 1D dataset is used to
;   create the second independent variable.
;
; PARAMETERS
;   dataRef [in;req] - an array of 1D datasets to be zipped.
;
;   pState [in;req] - the state structure
;
;   tlb [in;req] - widget id of tlb
;
; KEYWORDS:
;
function zipper, dataRef, pState, tlb

; Extract the x-axis data. For each,
;  - record size (nos of elements)
;  - record minimum and maximum values
;  - find and record the maximum bin width
;  - find and record the descriPtr
; Also determine
;  - the range for which datasets intersect
;  - whether datasets all have the same size
;  - whether datasets all have the same range
;  - whether datasets have the same xtype
nData = n_elements(dataRef)
xPtrArr = ptrarr(1)
xlen = intarr(nData)
xmin = fltarr(nData)
xmax = fltarr(nData)
bwmax = fltarr(nData)
dNames = strarr(nData)
dValues = fltarr(nData)
bwabs = 0.0
xminmax = 0.0
xmaxmin = 0.0
xtype = ''
sameXtype = 1b
sameSize = 1b
sameRange = 1b
sameDscPtrName = 1b
sameDscPtrValue = 0b
for i = 0,nData-1 do begin
    if (ptr_valid(dscPtr)) then ptr_free, dscPtr
    if ((dataRef[i])->get(xval=xval,xtype=xt,descriPtr=dscPtr)) then begin
        ;; descriPtr name should not be null
        if (strtrim((*dscPtr).name,2) eq '') then begin
            heap_free, xPtrArr
            ptr_free, dscPtr
            title = 'DATABROWSER - Combine Data'
            message = 'Some datasets have an empty tag name!'
            void = dialog_message(message,/error,title=title,dialog_parent=tlb)
            return, 0
        endif
        xlen[i] = n_elements(xval)
        xmin[i] = xval[0]
        xmax[i] = xval[xlen[i]-1]
        bwmax[i] = max(xval[1:xlen[i]-1] - xval[0:xlen[i]-2])
        dValues[i] = (*dscPtr).qty
        if (i eq 0) then begin
            xPtrArr[i] = ptr_new(xval)
            bwabs = bwmax[i]
            xminmax = xmin[i]
            xmaxmin = xmax[i]
            xtype = strupcase(strmid(xt,0,4)) ; only use first 4 characters of type --> 'HIST'/'POIN'
            dName = strtrim((*dscPtr).name,2)
        endif else begin
            xPtrArr = [xPtrArr,ptr_new(xval)]
            sameSize = sameSize and (xlen[i] eq xlen[i-1])
            sameRange = sameRange and ((xmin[i] eq xmin[i-1]) and (xmax[i] eq xmax[i-1]))
            sameXtype = sameXtype and (xtype eq strupcase(strmid(xt,0,4)))
            sameDscPtrName = sameDscPtrName and (strupcase(dName) eq strupcase(strtrim((*dscPtr).name,2)))
            sameDscPtrValue = sameDscPtrValue or (dValues[i] eq dValues[i-1])
            bwabs = bwmax[i] > bwabs
            xminmax = xminmax > xmin[i]
            xmaxmin = xmaxmin < xmax[i]
        endelse
    endif
endfor
nData = n_elements(xPtrArr)
ord = sort(dValues)

; 1) Proceed to zipping the data if all the following conditions are
; satisfied:
;  - x-axis values have same size, range and type
;  - all datasets have the same descriPtr names BUT different values
;
; 2) Otherwise check for following conditions
;   i) Exit immediately if
;     - xtypes are not the same
;     - the region of overlap of all datasets is less than twice the
;       largest bin width
;     - the desciPtr names are different or their values are the same
;   ii) if the sizes/ranges are different then rebin all data to the region of
;    overlap using the largest available bin width.
;
if ((not sameSize) or (not sameRange) or (not sameXtype) or $
    (not sameDscPtrName) or (sameDscPtrValue)) then begin

    ;; descriPtr variables must all have the same name
    if (not sameDscPtrName) then begin
        heap_free, xPtrArr
        ptr_free, dscPtr
        title = 'DATABROWSER - Combine Data'
        message = 'All datasets should have the same tag name!'
        void = dialog_message(message,/error,title=title,dialog_parent=tlb)
        return, 0
    endif

    ;; descriPtr variables must have different values
    if (sameDscPtrValue) then begin
        heap_free, xPtrArr
        ptr_free, dscPtr
        title = 'DATABROWSER - Combine Data'
        message = 'At least two datasets have the same tag value!'
        void = dialog_message(message,/error,title=title,dialog_parent=tlb)
        return, 0
    endif

    ;; X Types must be the same
    if (not sameXtype) then begin
        heap_free, xPtrArr
        ptr_free, dscPtr
        title = 'DATABROWSER - Combine Data'
        message = 'Datasets should all be of the same type (either points or histograms)!'
        void = dialog_message(message,/error,title=title,dialog_parent=tlb)
        return, 0
    endif

    ;; Determine maximum bin width within the overlap region
    bwmax = 0.0
    for i=0,nData-1 do begin
        xval = *(XPtrArr[i])
        ind = where((xval ge xminmax) and (xval le xmaxmin), npts)
        bwmax = bwmax > max(xval[ind[1]:ind[npts-1]] - xval[ind[0]:ind[npts-2]])
    endfor

    ;; make sure overlap region is okay
    if (xmaxmin-xminmax lt 2*bwmax) then begin
        heap_free, xPtrArr
        ptr_free, dscPtr
        title = 'DATABROWSER - Combine Data'
        message = 'Insufficient overlap between datasets hence they cannot be combined!'
        void = dialog_message(message,/error,title=title,dialog_parent=tlb)
        return, 0
    endif

    ;; Okay, we have gone through the main hurdles!

    ;; New output grid
    nx = fix((xmaxmin - xminmax)/bwmax) + 1
    xout = xminmax + findgen(nx)*bwmax
    yout = dValues[ord]
    strPtrArr = ptrarr(nData)

    ;; Do the rebinning
    for j = 0,nData-1 do begin
        i = ord[j]              ; order them in ascending descriPtr value
        void = (dataRef[i])->get(zval=zin,zerr=zein,treatment=trmt)
        xin = *(xPtrArr[i])
        case xtype of
            'HIST': drebin,xin,zin,zein,xout,zout,zeout,/hist,/to_hist,err=err,emsg=emsg
            else: drebin,xin,zin,zein,xout,zout,zeout,/points,/to_points,err=err,emsg=emsg
        endcase
        if (err ne 0) then begin
            !error_state.code = err
            !error_state.msg = emsg
            heap_free, xPtrArr
            heap_free, strPtrArr
            ptr_free, dscPtr
            void = dialog_message(emsg,/information,title=title,dialog_parent=tlb)
            return, 0
        endif
        if (j eq 0) then begin
            ztot = fltarr(n_elements(zout),nData)
            zetot = ztot
        endif
        ztot[*,j] = zout
        zetot[*,j] = zeout
        yout[j] = dValues[i]
        strPtrArr[j] = ptr_new(trmt)
    endfor
endif else begin
    xout = *(xPtrArr[0])
    yout = dValues[ord]
    strPtrArr = ptrarr(nData)
    for j=0,nData-1 do begin
        i = ord[j]
        void = (dataRef[i])->get(zval=zout,zerr=zeout,treatment=trmt)
        if (j eq 0) then begin
            ztot = fltarr(n_elements(zout),nData)
            zetot = ztot
        endif
        ztot[*,j] = zout
        zetot[*,j] = zeout
        strPtrArr[j] = ptr_new(trmt)
    endfor
endelse

; Now have 2D data! Get a davePtr,etc from the first dataset and use
; as template for the new dataset
if (not (dataRef[0])->get(davePtr=davePtr,treatment=trmt)) then begin
    heap_free, [xPtrArr, strPtrArr, dscPtr]
    void = dialog_message(!error_state.msg,/information,title=title,dialog_parent=tlb)
    return, 0
endif

; 2nd axis details
ylabel = (*dscPtr).legend
yunits = (*dscPtr).units
ytype = 'POINTS'

; modify and update descriPtr
(*dscPtr).legend = (*dscPtr).legend+' dependence'
(*dscPtr).qty = 0.0
(*dscPtr).err = 0.0
(*(*davePtr).descriPtr) = (*dscPtr)

; Construct new treatment log. Including all treatment info from
; the individual datasets used
treatment = ['Combine data: Session log modified on: '+systime(), $
             '_____________________________________________________________________', $
             'This dataset is created by combining (zipping) '+strtrim(string(nData),2)+' datasets.', $
             'The log information for these individual datasets are included below.']
for i=0,nData-1 do begin
    treatment = [treatment,' ', $
                 'Dataset '+strtrim(string(i),2)+' log =============================', $
                 *(strPtrArr[i])]
endfor

; update davePtr
(*(*(*davePtr).dataStrPtr).commonStr.histPtr) = {qty:ztot,err:zetot,x:xout,y:yout}
(*(*davePtr).dataStrPtr).commonStr.ytype = ytype
(*(*davePtr).dataStrPtr).commonStr.yunits = yunits
(*(*davePtr).dataStrPtr).commonStr.ylabel = ylabel
(*(*(*davePtr).dataStrPtr).commonStr.treatmentPtr) = treatment

; Create the DaveDat2D object
nameTag = 'Combined data '+strtrim(string((*pState).zipCount),2)
(*pState).zipCount = (*pState).zipCount + 1 
newObj = obj_new('DaveDat2D',davePtr=davePtr,nameTag=nameTag)

; Cleanup
heap_free, [xPtrArr, strPtrArr, davePtr]
if (obj_valid(newObj) eq 1) then begin
    void = newObj->set(descriPtr=dscPtr)
    ptr_free, dscPtr
endif else begin
    ptr_free, dscPtr
    void = dialog_message(!error_state.msg,/information,title=title,dialog_parent=tlb)
    return, 0
endelse

; Add object to the 2D data branch of tree widget and to the 2D data container
uname = 'TWOD_DATA'
id = widget_info(tlb,find_by_uname=uname)
new_leaf = widget_tree(id,value=nameTag,uvalue=newObj,uname='SP_TWOD_DATA',bitmap=(*pState).dataBMP)
(*pState).o_2d_cont->add,newObj

return, 1

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



;===============================================================================
; DB_i_zipOned
;
; PURPOSE:
;   Get datasets to be zipped from 1D tree
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_zipOned, event
;
; Get 1D datasets to be zipped
;

widget_control,event.top,get_uvalue=pState

; Get the object references of the selected datasets.
dataRef = ((*pState).sel_cont)->get(/all, count=nData)

; there must be more than one dataset
if (nData le 1) then return

; zip them up
res = zipper(dataRef, pstate, event.top)

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



;===============================================================================
; DB_i_zipTwod
;
; PURPOSE:
;   Get datasets to be zipped from 2D tree
;
; PARAMETERS
;   event [in] - the event structure. 
;
; KEYWORDS:
;
pro DB_i_zipTwod, event

; Get 2D datasets to be zipped
; Determine cut/slice to be used from the datasets

widget_control,event.top,get_uvalue=pState

; there must be more than one dataset
sel_cont = (*pState).sel_cont
nObj = sel_cont->count()
if (nObj le 1) then return

; Use the 'cut' dialog interface to determine cut parameters
s_out = db_dataCuts(sel_cont,group_leader=event.top,/zipper)
if s_out.cancel then return

; Make the 1d cuts/slices of the data and save the results in an
; object array
twodObjArr = sel_cont->get(/all)
onedObjArr = objarr(nobj)

center = s_out.center[0]
width = s_out.width[0]
cutaxis = s_out.cutaxis[0]
lo_lim = center-0.5*width
up_lim = center+0.5*width
for i = 0,nobj-1 do begin
    newObj = twodObjArr[i]->cut(cutaxis, lo_lim, up_lim)
    if (not obj_valid(newObj)) then begin
        title = 'DATABROWSER - Combine Data'
        void = dialog_message(!error_state.msg,/error,title=title,dialog_parent=tlb)
        heap_free, onedObjArr
        return
    endif
    onedObjArr[i] = newObj
endfor

; Zip them up
res = zipper(onedObjArr, pstate, event.top)

if (not res) then heap_free, onedObjArr

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



;===============================================================================
; DB_i_createNew1DPlot
;
; PURPOSE:
;   Greate a new 1D (multi-)plot
;
; PARAMETERS
;   pState [in;req] - the state structure
;
;   tlb [in;req] - widget id of tlb
;
; KEYWORDS:
;
pro createNew1DPlot, pState, tlb

; Get the object references of all of the data selected in the tree LEAVES.
; Use these to create the Dave Graphic Object
dataRef = ((*pState).sel_cont)->get(/all, count=nData)
oDaveGr = obj_new('DaveGr1D', dataRef=dataRef)
if (not obj_valid(oDaveGr)) then return

; set different default symbols and linestyles for the plot
valid_symbols = [6,4,5,7,2,1]
valid_lstyles = [0,1,2,3,4,5]
valid_colors = [[0,0,255],[255,0,0],[0,255,0],[0,128,0],[128,0,128] $
               ,[255,255,0],[255,0,255],[128,128,0],[128,0,0]]
slen = n_elements(valid_symbols)
llen = n_elements(valid_lstyles)
clen = (size(valid_colors))[1]
symbols = bytarr(nData)
lstyles = bytarr(nData)
colors = bytarr(nData,3)
for i=0,nData-1 do begin
    ;if (ind gt 5) then ind = 0
    symbols[i] = valid_symbols[i mod slen]
    lstyles[i] = valid_lstyles[i mod llen]
    colors[i,0:2] = valid_colors[0:2,i mod clen]
endfor
void = oDaveGr->set(symType=symbols,lineType=lstyles,color=colors)

; Add the object to the global '1-D Graphics Container'
(*pState).o_1d_gr_cont->add,oDaveGr

; Add a new '1-D Plot' leaf
void = oDaveGr->get(nameTag=display_name)
uname = 'ONED_PLOT'
id = widget_info(tlb,find_by_uname=uname)
new_leaf = widget_tree(id,value=display_name,uvalue=oDaveGr,uname='SP_ONED_PLOT',bitmap=(*pState).plot1DBMP)

; Create a view of the Dave Graphics object by launching an instance
; of the appropriate DB_Display1D
o = obj_new('DB_Display1D',group_leader=tlb, objGrRef=oDaveGr, workDir=(*pState).data_directory)

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



;===============================================================================
; DB_i_createNew2DPlot
;
; PURPOSE:
;   Greate a new 2D display (image, contour or surface) from the
;   selected dataset.
;
; PARAMETERS
;   pState [in;req] - the state structure
;
;   tlb [in;req] - widget id of tlb
;
;   wid [in;req] - id of widget making the request
;
; KEYWORDS:
;
pro createNew2DPlot, pState, tlb, wid

; Get the uvalue of the widget (to determine requested plot type)
widget_control,wid,get_uvalue=pType

; Get the object references of the data selected in the tree leaf.
; Create a new DaveGr2D object from the data
dataRef = ((*pState).sel_cont)->get(position=0)
plotTypes=['CONTOUR','IMAGE','SURFACE']
;plotType=plotTypes[(*pState).def2DPlotType]
plotType=plotTypes[pType]
oDaveGr = obj_new('DaveGr2D', dataRef=dataRef,plotType=plotType)
if (not obj_valid(oDaveGr)) then return

;void = oDaveGr->set(symType=symbols,lineType=lstyles,color=colors)

; Add the object to the global '2-D Graphics Container'
(*pState).o_2d_gr_cont->add,oDaveGr

; Create a view of the Dave Graphics object by launching an instance
; of the appropriate DB_Display2D* widget object
case plotType of
   'CONTOUR': oRef = obj_new('DB_Display2DC',group_leader=tlb,objGrRef=oDaveGr,workDir=(*pState).data_directory)
   'IMAGE': oRef = obj_new('DB_Display2DI',group_leader=tlb,objGrRef=oDaveGr,workDir=(*pState).data_directory)
   'SURFACE': oRef = obj_new('DB_Display2DS',group_leader=tlb,objGrRef=oDaveGr,workDir=(*pState).data_directory)
endcase
if (~obj_valid(oRef)) then return

; Add a new '2-D Plot' leaf
case plotType of
    'CONTOUR': bmapIcon = (*pState).plot2DCBMP
    'IMAGE': bmapIcon = (*pState).plot2DIBMP
    'SURFACE': bmapIcon = (*pState).plot2DSBMP
endcase
void = oDaveGr->get(nameTag=display_name)
uname = 'TWOD_PLOT'
id = widget_info(tlb,find_by_uname=uname)
new_leaf = widget_tree(id,value=display_name,uvalue=oDaveGr,uname='SP_TWOD_PLOT',bitmap=bmapIcon)


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



;===============================================================================
; DB_i_disp2Dgr
;
; PURPOSE:
;   Initiate display of a DaveGr2D object.
;
; PARAMETERS
;   pState [in;req] - the state structure
;
;   tlb [in;req] - widget id of tlb
;
; KEYWORDS:
;
pro disp2Dgr, pState, tlb

; get the selected object
objRef = ((*pState).sel_cont)->get(position=0)

; determine its plot type and launch the appropriate widget object
void = objRef->get(plotType=plotType)
case plotType of
    'CONTOUR': o = obj_new('DB_Display2DC',group_leader=tlb,objGrRef=oDaveGr,workDir=(*pState).data_directory)
    'IMAGE': o = obj_new('DB_Display2DI',group_leader=tlb,objGrRef=oDaveGr,workDir=(*pState).data_directory)
    'SURFACE': o = obj_new('DB_Display2DS',group_leader=tlb,objGrRef=oDaveGr,workDir=(*pState).data_directory)
endcase
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_i_ev
;
; PURPOSE:
;   Main event handler for app.
;
; PARAMETERS
;   event [in;req] - the event structure to be handled
;
; KEYWORDS:
;
pro DB_i_ev,event

; Basic error Handler
if (n_elements(!debug) && (!debug eq 0)) then begin
    catch, catchError
    if (catchError ne 0) then begin
        ;;print, 'Error handled!'
        eTitle = 'Data Browser: Error encountered'
        eMsg = 'An error or unusual condition was encountered!'
        eMsg = [eMsg,'Please, report the following to the DAVE team:']
        eMsg = [eMsg,!error_state.msg]
;    eMsg = [eMsg,'Function/module name: calledfunc()',!error_state.msg]
        void = dialog_message(/error,eMsg,title=eTitle,dialog_parent=event.top)
        catch, /cancel
        return
    endif
endif

; Main event handler/dispatcher
widget_control,event.top,get_uvalue=pState
uname = widget_info(event.id,/uname)

; First test if this is a context-sensitive menu launch event
this_event = tag_names(event,/structure)
if (this_event eq 'WIDGET_CONTEXT') and $
	(widget_info((*pState).cur_id,/valid_id)) then begin
	DB_i_context,event
endif

; Now consider the different events using the UNAME to sort them out.
switch uname of
    'QUIT': begin
        widget_control,event.top,/destroy
        break
    end

    'LOAD_DATA': begin
        DB_i_load,event
        break
    end

    'REMOVE_DATA':  begin
        DB_i_remove,event
        break
    end

    'HELP' : begin
        db_Help, event
        break
    end

    'EXTRACT_SPEC': begin
        DB_i_extractSpectra, event
        break
    end

    'RESET_DATA': begin
        DB_i_resetData, event
        break
    end

    'CLONE_DATA': begin
        DB_i_cloneData, event
        break
    end

    'DSCPTR_ONED':
    'DSCPTR_TWOD': begin
        DB_i_editdescriptr, event
        break
    end

    'ZIP_ONED': begin
        DB_i_zipOned, event
        break
    end

    'ZIP_TWOD': begin
        DB_i_zipTwod, event
        break
    end

    'DISPHIST_ONED':
    'DISPHIST_TWOD': begin
        DB_i_DispHist, event
        break
    end

    'DISPDATA_ONED':
    'DISPDATA_TWOD': begin
        DB_i_DispData, event
        break
    end

    'SP_ONED_DATA':
    'SP_TWOD_DATA':
    'SP_ONED_PLOT':
    'SP_TWOD_PLOT': begin
        DB_i_addToSelCont,event
        break
    end

    'NEW_ONED_PLOT': begin
        createNew1DPlot, pState, event.top
        break
    end

    'NEW_TWOD_PLOT': begin
        createNew2DPlot, pState, event.top, event.id
        break
    end

    'DISP_ONEDGR': begin
        o=obj_new('DB_Display1D',group_leader=event.top,workDir=(*pState).data_directory)
        break
    end

    'DISP_TWODGR': begin
        disp2Dgr, pState, event.top
        break
    end

    'ANIMATOR': begin
        void = dialog_message("Coming soon...",/information,title="DATABROWSER: Animator",dialog_parent=event.top)
        ;;		o = obj_new('db_animator',group_leader=event.top)
        break
    end

    'REBIN': begin
        DB_i_rebin,event
        break
    end

    'TRANSPOSE': begin
        DB_i_transpose,event
        break
    end

    'CUT': begin
        DB_i_cut,event
        break
    end

    'CHANGE_NTAG_ONED':
    'CHANGE_NTAG_TWOD':
    'CHANGE_NTAG_ONEDP':
    'CHANGE_NTAG_TWODP': begin
        DB_i_change_name,event
        break
    end

    'DATA_OFFSET': begin
        DB_i_offset,event
        break
    end

    'DATA_SFACTOR': begin
        DB_i_sfactor,event
        break
    end

    'LAUNCH_PAN_ONED':
    'LAUNCH_PAN_TWOD': begin
        DB_i_pan,event
        break
    end

    'SAVE_ONED_3COL': begin
        DB_i_save2ASCII3COL, event
        break
    end
    'SAVE_TWOD_GRP': begin
        DB_i_save2ASCIIGRP, event        
        break
    end
    'SAVE_TWOD_SPE': begin
        DB_i_save2ASCIISPE, event        
        break
    end

    'SAVE_ONED_DAVE':
    'SAVE_TWOD_DAVE': begin
        DB_i_save2Dave, event
        break
    end

    else:
endswitch

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



;===============================================================================
; DB_i_loadDataFromDavePtrs
;
; PURPOSE:
;   Usually, data is loaded into the Data Browser directly from a
;   file. This procedures allows data to be received from another
;   application module within DAVE.
;
; PARAMETERS
;   tlb [in;req] - widget id of tlb
;
; KEYWORDS:
;   data [in;req] - an array of davePtr datasets to be loaded
;
;   nameTagArr [in] - optional array of nametags to be used for
;                  identifying the datasets in the data tree widget.
;
pro DB_i_loadDataFromDavePtrs, tlb, data=davePtrArr, nametag=nametagArr

widget_control, tlb, get_uvalue=pstate

ndat = n_elements(davePtrArr)
if (ndat le 0) then return

ntag = n_elements(nametagArr)
i = 0
; make sure no nametag is an empty string
if (ntag gt 0) then begin
    for j=0,ntag-1 do begin
        if (strtrim(nametagArr[j],2) eq '') then begin
            nametagArr[j] = 'Untitled'+strtrim(string(i),2)
            i = i + 1
        endif
    endfor
endif

; make sure there are as many nametags as datasets
if (ntag lt ndat) then begin
    nametagArr = (ntag eq 0)? strarr(ndat) : [nametagArr,strarr(ndat-ntag)]
    while (ntag lt ndat) do begin
        nametagArr[ntag] = 'Untitled'+strtrim(string(i),2)
        ntag = ntag + 1
        i = i + 1
    endwhile
endif

; loop through davePtr array and load the data
for i = 0,ndat-1 do begin
    davePtr = davePtrArr[i]
    nameTag = nametagArr[i]
    ;; does this davePtr points to a valid davePtr structure?
    if (not ptr_valid(davePtr)) then continue
    if (vartype(*davePtr) ne 'DAVEPTRSTR') then continue

    ;; does it contain data
    if (not ptr_valid((*(*davePtr).dataStrPtr).commonStr.histPtr)) then continue
    if (n_elements(*(*(*davePtr).dataStrPtr).commonStr.histPtr) eq 0) then continue

    ;; get dimensions of data; should be 1 or 2
    dim = (size((*(*(*davePtr).dataStrPtr).commonStr.histPtr).qty))[0]
    if ((dim ne 1) and (dim ne 2)) then continue

    ;; Create the proper DAVE Data Object from the DAVE Pointer depending
    ;; on the type of data it contains.  Put the object into the
    ;; appropriate container object.
    if (dim eq 1) then begin	; 1-d data
        o1 = obj_new('DaveDat1D',davePtr=davePtr, nameTag=nameTag)
        (*pstate).o_1d_cont->add,o1
        ;; Add a leaf
        id = widget_info(tlb,find_by_uname='ONED_DATA')
        new_leaf = widget_tree(id,value=nameTag,uvalue=o1,uname='SP_ONED_DATA',bitmap=(*pState).dataBMP)
    endif else begin            ; 2-d data
        o2 = obj_new('DaveDat2D',davePtr=davePtr,nameTag=nameTag)
        (*pState).o_2d_cont->add,o2
        ;; Add a leaf
        id = widget_info(tlb,find_by_uname='TWOD_DATA')
        new_leaf = widget_tree(id,value=nameTag,uvalue=o2,uname='SP_TWOD_DATA',bitmap=(*pState).dataBMP)
    endelse
endfor

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



;===============================================================================
; DB_help
;
; PURPOSE:
;   Launch a pdf help manual/tutorial for the app.
;
; PARAMETERS
;   event [in;req] - the event structure to be handled
;
; KEYWORDS:
;
pro DB_help,event
void = launch_help(!DAVE_AUXILIARY_DIR+'databrowser.pdf',tlb = event.top)
return
end
;-------------------------------------------------------------------------------



;===============================================================================
; DB_interface
;
; PURPOSE:
;   Entry point and dialog builder for Data Browser application
;   module.
;
; PARAMETERS
;
; KEYWORDS:
;   group_leader [in] - the ID of the parent widget from which this
;                       app was launched.
;
;   working_directory [in] - user's working directory setting from
;                            DAVE.
;
;   _EXTRA [in] - allows keywords to be passed directly to
;                 DB_i_loadDataFromDavePtrs which can load existing
;                 davePtr data directly into the application at
;                 startup rather than subsequently from the 'Load
;                 Data' button.
;
pro DB_interface, group_leader=group_leader, working_directory=working_directory $
                  ,dbTLB=tlb, _EXTRA=etc

register_name = 'DaveDataBrowser'
if xregistered(register_name) then return

; Get the current colors
tvlct,r_orig,g_orig,b_orig,/get
device,get_decomposed = dc_orig
device,decomposed = 0

; Widget definition module
if n_elements(group_leader) eq 0 then group_leader = 0L
tlb = widget_base(/row,title = $
	'DATABROWSER-Data Analysis and Visualization Environment', $
	group_leader = group_leader,map = 0)

; Build up the button column base: this is the bulletin board base
xsize1 = 200 & xsize2=300 & ysize = 350
ygap = 50
xoset = 10
yoset = 10
col_1 = widget_base(tlb,xsize=xsize1,ysize=ysize,/align_center)
nbuttons = 4
b = lonarr(nbuttons)
button_base = lonarr(nbuttons)

tooltip='Load new DAVE format data into the application'
n = 0
button_base[n] = widget_base(col_1,map=0,xoffset=xoset,yoffset=yoset+n*ygap)
b[n] = widget_button(button_base[n],value='Load Data',uname='LOAD_DATA',tooltip=tooltip)

tooltip='Delete the data and/or plots that are currently highlighted'
n = 1
button_base[n] = widget_base(col_1,map=0,xoffset=xoset,yoffset=yoset+n*ygap)
b[n] = widget_button(button_base[n],value='Remove Selected Data/Plot',uname='REMOVE_DATA',tooltip=tooltip)
butinfo = widget_info(b[1],/geometry)
butXsize=butinfo.xsize
widget_control,b[0],xsize=butXsize

tooltip = "Display user manual"
n = 2
button_base[n] = widget_base(col_1,map=0,xoffset=xoset,yoffset=yoset+n*ygap)
b[n] = widget_button(button_base[n],value='User Manual',uname='HELP',xsize=butXsize,tooltip=tooltip)


tooltip='Exit DATABROWSER'
n = 3
button_base[n] = widget_base(col_1,map=0,xoffset=xoset,yoffset=yoset+n*ygap)
b[n] = widget_button(button_base[n],value='Quit',uname='QUIT',xsize=butXsize,tooltip=tooltip)

;win = widget_draw(col_1,units = 1,xsize = 2,ysize = 3)
win = widget_draw(col_1,xsize = xsize1,ysize = ysize)

; Create the tree widget
;col_2 = widget_base(tlb,xsize = xsize2,ysize = ysize)
tree = widget_tree(tlb,/context_events,/multiple,uname='TREE',xsize=xsize2,ysize=ysize)
root = widget_tree(tree,value = 'DAVE WORKSPACE',/folder,/expanded,uname = 'ROOT')
leaf_1d = widget_tree(root,value = '1-D Data',/folder,/expanded, $
	uname = 'ONED_DATA')
leaf_2d = widget_tree(root,value = '2-D Data',/folder,/expanded, $
	uname = 'TWOD_DATA')
leaf_1dgr = widget_tree(root,value = '1-D Plots',/folder,/expanded, $
	uname = 'ONED_PLOT')
leaf_2dgr = widget_tree(root,value = '2-D Plots',/folder,/expanded, $
	uname = 'TWOD_PLOT')


; Create the context menus
;---------------------------

; Tooltips
cnTooltip = 'Modify the display label for this item.'
rdTooltip = 'Modify the range and/or bin width of the selected data.'
ofTooltip = 'Modify the vertical offset of the selected data.'
sfTooltip = 'Modify the vertical scalefactor of the selected data.'
ctTooltip = 'Make cuts/slices from the selected data'
clTooltip = 'Clone the selected data.'
exTooltip = 'Extract spectra along x or y-axis.'
rsTooltip = 'Reset the selected data to their pristine state.'
n1dTooltip = 'Create a new (multi-)plot from the selected data.'
panTooltip = 'Fit the selected data using PAN.'
dpTooltip = 'Display the selected plot.'
ptTooltip = 'Modify the default 2D plot type for creating new 2D plots.'
n2dTooltip = 'Create a new 2D plot of the current data.'
dscTooltip = 'Modify sample information tag - used when combining datasets'
tmtTooltip = 'View data reduction (treatment) history'
ddTooltip = 'View actual raw data'
zp1Tooltip = 'Create an aggregate 2D dataset using multiple 1D datasets'
zp2Tooltip = 'Create an aggregate 2D dataset using cuts/slices from multiple 2D datasets'
outTooltip = 'Save the selected dataset in a DAVE format file'
outTooltip1 = 'Save the selected dataset in an ASCII 3 column format file'
outTooltip2 = 'Save the selected dataset in an ASCII group format file'
outTooltip3 = 'Save the selected dataset in an ASCII SPE format file'
transTooltip = 'Swap the independent axes'

; 1-d Data
one_d_base = widget_base(tlb,/context_menu,uname = 'ONED_CONTEXT_BASE')
void = widget_button(one_d_base,value='View Treatment Log',uname='DISPHIST_ONED',tooltip=tmtTooltip)
void = widget_button(one_d_base,value='View Raw Data',uname='DISPDATA_ONED',tooltip=ddTooltip)
void = widget_button(one_d_base,value='Modify Data Label',uname='CHANGE_NTAG_ONED',tooltip=cnTooltip,/separator)
void = widget_button(one_d_base,value='Modify Data Offset',uname='DATA_OFFSET',tooltip=ofTooltip)
void = widget_button(one_d_base,value='Modify Data Scalefactor',uname='DATA_SFACTOR',tooltip=sfTooltip)
void = widget_button(one_d_base,value='Sample Information Tag',uname='DSCPTR_ONED',tooltip=dscTooltip)
void = widget_button(one_d_base,value='Rebin Data',uname='REBIN',tooltip=rdTooltip,/separator)
void = widget_button(one_d_base,value='Revert to Original',uname='RESET_DATA',tooltip=rsTooltip)
void = widget_button(one_d_base,value='Clone Data',uname='CLONE_DATA',/separator,tooltip=clTooltip)
void = widget_button(one_d_base,value='Combine Selected Data',uname='ZIP_ONED',tooltip=zp1Tooltip)
void = widget_button(one_d_base,value='Create New 1-D (Multi) Plot',uname='NEW_ONED_PLOT',/separator,tooltip=n1DTooltip)
void = widget_button(one_d_base,value='Animator',uname='ANIMATOR',sensitive=0)
void = widget_button(one_d_base,value='Launch PAN',uname='LAUNCH_PAN_ONED',tooltip=panTooltip)
void = widget_button(one_d_base,value='Save data (ASCII 3 Columns)',uname='SAVE_ONED_3COL',/separator,tooltip=outTooltip1)
void = widget_button(one_d_base,value='Save data (DAVE format)',uname='SAVE_ONED_DAVE',tooltip=outTooltip)

; 1-d Plot
one_d_gr_base = widget_base(tlb,/context_menu,uname='ONEDGR_CONTEXT_BASE')
void = widget_button(one_d_gr_base,value='Modify Plot Label',uname='CHANGE_NTAG_ONEDP',tooltip=cnTooltip)
void = widget_button(one_d_gr_base,value='View Plot',uname='DISP_ONEDGR',tooltip=dpTooltip)

; 2-d Data
two_d_base = widget_base(tlb,/context_menu,uname='TWOD_CONTEXT_BASE')
void = widget_button(two_d_base,value='View Treatment Log',uname='DISPHIST_TWOD',tooltip=tmtTooltip)
void = widget_button(two_d_base,value='View Data',uname='DISPDATA_TWOD',tooltip=ddTooltip)
void = widget_button(two_d_base,value='Modify Data Label',uname='CHANGE_NTAG_TWOD',tooltip=cnTooltip,/separator)
void = widget_button(two_d_base,value='Modify Data Scalefactor',uname='DATA_SFACTOR',tooltip=sfTooltip)
void = widget_button(two_d_base,value='Sample Information Tag',uname='DSCPTR_TWOD',tooltip=dscTooltip)
void = widget_button(two_d_base,value='Rebin Data',uname='REBIN',tooltip=rdTooltip,/separator)
void = widget_button(two_d_base,value='Transpose Data',uname='TRANSPOSE',tooltip=transTooltip)
void = widget_button(two_d_base,value='Revert to Original',uname='RESET_DATA',tooltip=rsTooltip)
void = widget_button(two_d_base,value='Cut Data',uname='CUT',/separator,tooltip=ctTooltip)
void = widget_button(two_d_base,value='Extract Spectra',uname='EXTRACT_SPEC',tooltip=exTooltip)
void = widget_button(two_d_base,value='Clone Data',uname='CLONE_DATA',tooltip=clTooltip)
void = widget_button(two_d_base,value='Combine Selected Data',uname='ZIP_TWOD',tooltip=zp2Tooltip)
newp = widget_button(two_d_base,value='Create New 2-D Plot',uname='NEW_TWOD_PLOT',/separator,tooltip=n2dTooltip,/menu)
void = widget_button(newp,value='Contour',uname='NEW_TWOD_PLOT',uvalue=0b)
void = widget_button(newp,value='Image',uname='NEW_TWOD_PLOT',uvalue=1b)
void = widget_button(newp,value='Surface',uname='NEW_TWOD_PLOT',uvalue=2b)

void = widget_button(two_d_base,value='Animator',uname='ANIMATOR',sensitive=0)
void = widget_button(two_d_base,value='Launch PAN',uname='LAUNCH_PAN_TWOD',tooltip=panTooltip)
void = widget_button(two_d_base,value='Save data (ASCII GRP format)',uname='SAVE_TWOD_GRP',/separator,tooltip=outTooltip2)
void = widget_button(two_d_base,value='Save data (ASCII SPE format)',uname='SAVE_TWOD_SPE',tooltip=outTooltip3)
void = widget_button(two_d_base,value='Save data (DAVE format)',uname='SAVE_TWOD_DAVE',tooltip=outTooltip)

; 2-d Plot
one_d_gr_base = widget_base(tlb,/context_menu,uname='TWODGR_CONTEXT_BASE')
void = widget_button(one_d_gr_base,value='Modify Plot Label',uname='CHANGE_NTAG_TWODP',tooltip=cnTooltip)
void = widget_button(one_d_gr_base,value='View Plot',uname='DISP_TWODGR',tooltip=dpTooltip)

widget_control,tlb,/realize,map = 0

; Load bitmaps to be used as icons
window,/free,/pixmap,xsize=16,ysize=16
winPix = !d.window

delim = path_sep()
bmp = read_bmp(!DAVE_AUXILIARY_DIR+'dm.bmp',r,g,b)
tvlct,r,g,b
tv,bmp
dataBMP = tvrd(true=3)

bmp = read_bmp(!DAVE_AUXILIARY_DIR+'plot.bmp',r,g,b)
tvlct,r,g,b
tv,bmp
plot1DBMP = tvrd(true=3)

bmp = read_bmp(!DAVE_AUXILIARY_DIR+'image.bmp',r,g,b)
tvlct,r,g,b
tv,bmp
plot2DIBMP = tvrd(true=3)

bmp = read_bmp(!DAVE_AUXILIARY_DIR+'contour.bmp',r,g,b)
tvlct,r,g,b
tv,bmp
plot2DCBMP = tvrd(true=3)

bmp = read_bmp(!DAVE_AUXILIARY_DIR+'surface.bmp',r,g,b)
tvlct,r,g,b
tv,bmp
plot2DSBMP = tvrd(true=3)
wdelete, winPix
widget_control,win,get_value=win_vis
loadct,15,/silent

; Make some nice data to display
data = DB_i_create_pretty_plot()

widget_control,col_1,map = 0,sensitive = 0
wset,win_vis
tv,bytscl(congrid(data,xsize1,ysize))
for i = 0,nbuttons-1 do begin
  widget_control,button_base[i],/map
endfor
widget_control,col_1,/map,/sensitive
widget_control,tlb,/map

; load original colors that were available prior to
; running this pro
tvlct,r_orig,g_orig,b_orig
device,decomposed = dc_orig


state =	{group_leader:group_leader, $ ; might not need this?
         o_registry:obj_new('object_registry'), $
         
         ;; The 1-D and 2-D container objects contain all of the data currently loaded
         ;; into the DATA BROWSER.
         o_1d_cont:obj_new('IDL_CONTAINER'), $
         o_2d_cont:obj_new('IDL_CONTAINER'), $
         
         ;; The 1-D and 2-D gr container objects contain all the plots
         ;; that are currently present.
         o_1d_gr_cont:obj_new('IDL_CONTAINER'), $
         o_2d_gr_cont:obj_new('IDL_CONTAINER'), $

         ;; The SEL_CONT object is a container object that will contain the object
         ;; references of all of the data currently in the selected LEAVES of the tree.
         ;; This container can then be passed to one of the DATA BROWSER sub-applications
         ;; where the object references can be pulled out.
         sel_cont:obj_new('IDL_CONTAINER'), $

         ;; We don't want multiple views of the same DaveGr1D object so use
         ;; o_active_gr_cont  container to register DaveGr objects that are
         ;; being viewed. This info will then be used to prevent multiple views
         o_active_gr1D_cont:obj_new('IDL_CONTAINER'), $
         o_active_gr2DC_cont:obj_new('IDL_CONTAINER'), $
         o_active_gr2DI_cont:obj_new('IDL_CONTAINER'), $
         o_active_gr2DS_cont:obj_new('IDL_CONTAINER'), $

         ;; CUR_ID is a longword integer that represents the widget id for the
         ;; currently-selected leaf.
         cur_id:0L, $
         r:r_orig,g:g_orig,b:b_orig,dc:dc_orig, $
         cur_sel_ptr:ptr_new(/allocate_heap), $
         pan_container:obj_new('IDL_CONTAINER'), $

         ;; icon data for tree widget
         dataBMP:dataBMP, $
         plot1DBMP:plot1DBMP, $
         plot2DIBMP:plot2DIBMP, $
         plot2DCBMP:plot2DCBMP, $
         plot2DSBMP:plot2DSBMP, $

         ;; The default 2D plot type is 'CONTOUR' (0)
         def2DPlotType:0b, $
         zipCount:1, $         ; nos of new zipped datasets that have been created in current session
         data_directory:working_directory $
        }

pState = ptr_new(state,/no_copy)
widget_control,tlb,set_uvalue = pState
xmanager,register_name,tlb,/no_block,cleanup = 'DB_i_cleanup', $
         event_handler = 'DB_i_ev'


; If any datasets were passed in as parameters, load them in
if n_elements(etc) then $
  DB_i_loadDataFromDavePtrs, tlb, _EXTRA=etc

end


