; $Id: sample_props_widget.pro,v 1.12 2005/02/03 18:05:02 azuah Exp $
;###############################################################################
; 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.
;
;###############################################################################
; Written by J.R.D. Copley
;************************************************************************************************
pro sample_props_widget_cleanup,tlb
;************************************************************************************************
;
compile_opt strictarr
;
widget_control,tlb,get_uvalue = state
ptr_free,state.formulaePtr
ptr_free,state.densitiesPtr
ptr_free,state.packfracsPtr
ptr_free,state.resultsPtr
ptr_free,state.xsecsPtr
;
end


;************************************************************************************************
pro spw_displayresults_event,event
;************************************************************************************************
; This routine handles events generated by the "detailed results" widget.
compile_opt strictarr
;
;
widget_control,event.top,get_uvalue=state2
if (event.id eq state2.done) then begin
	*state2.sortedptr=2
	widget_control,event.top,set_uvalue=state2
	widget_control,event.top,/destroy
	return
endif
;
if (event.id eq state2.toggle) then begin
	*state2.sortedptr=1-*state2.sortedptr
	widget_control,event.top,set_uvalue=state2
	widget_control,event.top,/destroy
	return
endif
;
end


;************************************************************************************************
pro spw_displayresults,resultsPtr,formula,event
;************************************************************************************************
; This routine creates a "detailed results" modal widget.
;
compile_opt strictarr
;
;
ssize=get_screen_size()
xsize=ssize[0]*0.6/9
nbits=n_elements((*resultsPtr).natoms)
format='(f10.3)'
widget_control,event.top,get_uvalue=state
sorted=state.sorted
sortedptr=ptr_new(sorted)
;
; Toggling between detailed unsorted results and sorted results brings control back to this point.
again:
*sortedptr=sorted
;
tlb=widget_base(title='Details of calculation for '+formula,/col,/modal,group_leader=event.top)
	base1=widget_base(tlb,/row)
		base=lonarr(9)
		message=["Isotope","No. atoms","At. weight.","At. weight",$
			"Scattering","Scattering","Absorption","Absorption","Total"]
		message2=["","","per atom","summed",$
			"per atom (b)","summed (b)","per atom (b)","summed (b)","summed (b)"]
		for k=0,8 do begin
			base[k]=widget_base(base1,/col,xsize=xsize,/frame)
				void=widget_label(base[k],value=message[k])
				void=widget_label(base[k],value=message2[k])
				totatoms=0
				if (not sorted) then begin
					for j=0,nbits-1 do begin
						natoms=(*resultsPtr).natoms[j]
						totatoms=totatoms+natoms
						case k of
							0: void=widget_label(base[k],value=(*resultsPtr).isotope[j])
							1: void=widget_label(base[k],value=string(natoms,format=format))
							2: void=widget_label(base[k],value=string((*resultsPtr).atwt[j],format=format))
							3: void=widget_label(base[k],value=string((*resultsPtr).atwt[j]*natoms,format=format))
							4: void=widget_label(base[k],value=string((*resultsPtr).xssct[j],format=format))
							5: void=widget_label(base[k],value=string((*resultsPtr).xssct[j]*natoms,format=format))
							6: void=widget_label(base[k],value=string((*resultsPtr).xsabs[j],format=format))
							7: void=widget_label(base[k],value=string((*resultsPtr).xsabs[j]*natoms,format=format))
							8: void=widget_label(base[k],value=string(((*resultsPtr).xssct[j]+(*resultsPtr).xsabs[j])*natoms,format=format))
							else:
						endcase
					endfor
				endif else begin
					sum_same,(*resultsPtr).isotope,(*resultsPtr).natoms,tot_isotope,tot_atoms,srt
					nsrt=n_elements(srt)
					for jsrt=0,nsrt-1 do begin
						j=srt[jsrt]
						natoms=tot_atoms[jsrt]
						totatoms=totatoms+natoms
						case k of
							0: void=widget_label(base[k],value=(*resultsPtr).isotope[j])
							1: void=widget_label(base[k],value=string(natoms,format=format))
							2: void=widget_label(base[k],value=string((*resultsPtr).atwt[j],format=format))
							3: void=widget_label(base[k],value=string((*resultsPtr).atwt[j]*natoms,format=format))
							4: void=widget_label(base[k],value=string((*resultsPtr).xssct[j],format=format))
							5: void=widget_label(base[k],value=string((*resultsPtr).xssct[j]*natoms,format=format))
							6: void=widget_label(base[k],value=string((*resultsPtr).xsabs[j],format=format))
							7: void=widget_label(base[k],value=string((*resultsPtr).xsabs[j]*natoms,format=format))
							8: void=widget_label(base[k],value=string(((*resultsPtr).xssct[j]+(*resultsPtr).xsabs[j])*natoms,format=format))
							else:
						endcase
					endfor
				endelse
				void=widget_label(base[k],value=' ')
			case k of
				0: void=widget_label(base[k],value='TOTALS')
				1: void=widget_label(base[k],value=string(totatoms,format=format))
				3: void=widget_label(base[k],value=string((*resultsPtr).molwt,format=format))
				5: void=widget_label(base[k],value=string((*resultsPtr).tot_xssct,format=format))
				7: void=widget_label(base[k],value=string((*resultsPtr).tot_xsabs,format=format))
				8: void=widget_label(base[k],value=string((*resultsPtr).tot_xssct+(*resultsPtr).tot_xsabs,format=format))
				else: void=widget_label(base[k],value=' ')
			endcase
		endfor
		base2=widget_base(tlb,/row)
	done=widget_button(base2,value='Done')
	;
	if (not sorted) then begin
		toggletitle='Show sorted, simplified results'
	endif else begin
		toggletitle='Show unsorted results'
	endelse
	toggle=widget_button(base2,value=toggletitle)
	state2={done:done,toggle:toggle,sortedptr:sortedptr}
	widget_control,tlb,set_uvalue=state2
	widget_control,tlb,/realize
	;
	xmanager,'spw_displayresults',tlb
	;
	if (*sortedptr eq 2) then begin
		widget_control,event.top,set_uvalue=state
		ptr_free,sortedptr
		return
	endif
	state.sorted=*sortedptr
	widget_control,event.top,set_uvalue=state
	sorted=state.sorted
	goto,again
end


;************************************************************************************************
pro sample_props_docalc,event
;************************************************************************************************
; This routine is called in response to the "Calculate" button or in circumstances where a
; calculation is called for.
;
compile_opt strictarr
;
;
widget_control,event.top,get_uvalue=state
;
xsecsPtr=state.xsecsPtr
;
; Get the cross sections.
sample_properties,state.formula,wavelength=state.wavelength,database=-1,$
	natoms,isotope,atwt,xssct,xsabs,molwt,tot_xssct,tot_xsabs,$
	xsecsPtr=xsecsPtr,inconly=1-state.preference[2]
if (size(molwt,/tname) eq "STRING") then begin
	res=dialog_message(["Unacceptable formula: "+state.formula,molwt])
		return
endif
;
state.xsecsPtr=xsecsPtr
;
; If using crystal information and number density has been obtained,
;	compute the mass density.  Otherwise, use the given mass density and
;	compute the number density.

if (state.preference[3] eq 0 and state.rhon gt 0.0) then begin
	rhon=state.rhon
	density=rhon*molwt/state.avonum
	widget_control,state.getdensity,set_value=string(density,format='(f8.3)')
endif else begin
	density=state.density
	rhon=density*state.avonum/molwt
	widget_control,state.shownumden,set_value=string(rhon,format='(f8.5)')
endelse
;
;	Apply the packing fraction.
packfrac=state.packfrac
rhonpf=rhon*packfrac
;
; Write the results of the calculation.
widget_control,event.top,tlb_set_title=state.tlbtitle+" for "+state.formula
widget_control,state.result[0],set_value=string(molwt)
widget_control,state.result[1],set_value=string(rhon)
widget_control,state.result[2],set_value=string(density)
widget_control,state.result[3],set_value=string(tot_xssct)
widget_control,state.result[4],set_value=string(tot_xsabs)
widget_control,state.result[5],set_value=string(tot_xssct+tot_xsabs)
widget_control,state.result[6],set_value=string(state.wavelength)
widget_control,state.result[7],set_value=string(rhonpf)
widget_control,state.result[8],set_value=string(density*state.packfrac)
;
;	Macroscopic cross sections are written according to the user's preferences.
if (state.preference[0] eq 0 and state.preference[1] eq 0) then begin
	widget_control,state.result[9],set_value=string(10./(tot_xssct*rhonpf))
	widget_control,state.result[10],set_value=string(10./(tot_xsabs*rhonpf))
	widget_control,state.result[11],set_value=string(10./((tot_xssct+tot_xsabs)*rhonpf))
endif
if (state.preference[0] eq 1 and state.preference[1] eq 0) then begin
	widget_control,state.result[9],set_value=string(1./(tot_xssct*rhonpf))
	widget_control,state.result[10],set_value=string(1./(tot_xsabs*rhonpf))
	widget_control,state.result[11],set_value=string(1./((tot_xssct+tot_xsabs)*rhonpf))
endif
if (state.preference[0] eq 0 and state.preference[1] eq 1) then begin
	widget_control,state.result[9],set_value=string(0.1*(tot_xssct*rhonpf))
	widget_control,state.result[10],set_value=string(0.1*(tot_xsabs*rhonpf))
	widget_control,state.result[11],set_value=string(0.1*((tot_xssct+tot_xsabs)*rhonpf))
endif
if (state.preference[0] eq 1 and state.preference[1] eq 1) then begin
	widget_control,state.result[9],set_value=string(tot_xssct*rhonpf)
	widget_control,state.result[10],set_value=string(tot_xsabs*rhonpf)
	widget_control,state.result[11],set_value=string((tot_xssct+tot_xsabs)*rhonpf)
endif
;
;	Store the results.
ptr_free,state.resultsPtr
state.resultsPtr=ptr_new({molwt:molwt,rhon:rhon,tot_xssct:tot_xssct,tot_xsabs:tot_xsabs,$
	natoms:natoms,isotope:isotope,atwt:atwt,xssct:xssct,xsabs:xsabs})
;
;	If this is essentially the first calculation, create and display the disclaimer
;	and the "Show details" button.
if (state.details eq 0) then begin
	geom=widget_info(state.base2,/geometry)
	xsize=geom.xsize
	string1=$
			'Within this application total scattering cross sections are obtained by '+$
			'summing the total (coherent plus incoherent) bound atom scattering cross '+$
			'sections of the constituent atoms. (There is also an option to exclude the '+$
			'coherent scattering from the total scattering cross section.)  This procedure '+$
			'is never completely correct. In general the scattering cross section, which '+$
			'is the double differential scattering cross section integrated over all '+$
			'directions and all final energies, is a function, inter alia, of the incident '+$
			'neutron energy, the temperature of the material, the morphology of the '+$
			'material (e.g. powder, amorphous, single crystal etc), in some cases the '+$
			'orientation of the material, and the chemical and magnetic structures of the '+$
			'material. (Magnetic scattering is not taken into consideration.) For non-'+$
			'hydrogenous materials the procedure is probably (possibly) justified. For '+$
			'hydrogenous materials it should be used with considerable caution.'
		width = get_string_width(string1)
	ysize1 = 1+fix(float(width)/float(xsize))
	string2=$
		'Total absorption cross sections are obtained by summing the '+$
		'absorption cross sections of the constituent atoms. To the extent that the cross section goes as 1/v this is OK, but '+$
		'for nuclides that have an (n,gamma) resonance at thermal neutron energies, e.g. 113Cd, it is not correct.'
	width = get_string_width(string2)
	ysize2 = 1+fix(float(width)/float(xsize))
	DISCLAIMER=['DISCLAIMER',string1,string2]
	void=widget_text(state.base2,/wrap,value=DISCLAIMER,ysize=1+ysize1+ysize2)
	base2a=widget_base(state.base2,/row)
	state.details=widget_button(base2a,value='Show details of calculation')
endif
;
; Update the database (in memory).
nvals=widget_info(state.getformula_old,/droplist_number)
if (nvals eq 0) then begin
	formulae=state.formula
	densities=state.density
	packfracs=state.packfrac
endif else begin
	index=where(*state.formulaePtr eq state.formula)
	if (index eq -1) then begin
		formulae=[*state.formulaePtr,state.formula]
		densities=[*state.densitiesPtr,state.density]
		packfracs=[*state.packfracsPtr,state.packfrac]
		indices=sort(formulae)
		formulae=formulae[indices]
		densities=densities[indices]
		packfracs=packfracs[indices]
		*state.formulaePtr=formulae
		*state.densitiesPtr=densities
		*state.packfracsPtr=packfracs
	endif else begin
		(*state.densitiesPtr)[index]=state.density
		(*state.packfracsPtr)[index]=state.packfrac
	endelse
endelse
;
widget_control,state.getformula_old,set_value=formulae
;
widget_control,event.top,set_uvalue=state
;
end


;************************************************************************************************
pro sample_props_restore,event
;************************************************************************************************
; Respond to button that restores database.
;
compile_opt strictarr
;
;
widget_control,event.top,get_uvalue=state
;
restore,file=state.file
*state.formulaePtr=formulae
*state.densitiesPtr=densities
*state.packfracsPtr=packfracs
state.formula=formulae[0]
state.density=densities[0]
state.packfrac=packfracs[0]
widget_control,state.getformula_old,set_value=formulae
widget_control,state.getformula_new,set_value=formulae[0]
widget_control,state.getdensity,set_value=string(densities[0],format='(f8.3)')
widget_control,state.getpackfrac,set_value=string(packfracs[0],format='(f8.3)')
widget_control,event.top,set_uvalue=state
sample_props_widget_event,{id:state.docalc,top:event.top,handler:0l}
;
end


;************************************************************************************************
pro sample_props_widget_event,event
;************************************************************************************************
; This handles the remaining events.
;
compile_opt strictarr
;
;
widget_control,event.top,get_uvalue=state
;
modify_output=0
;
docalculation=0
;
;	The first two events set the "modify_output" flag.
; Handle mm or cm user preference menu item.
if (event.id eq state.prefmm) then begin
	temp=widget_info(event.id,/button_set)
	state.preference[0]=temp
	widget_control,event.id,set_button=1-temp
	modify_output=1
endif
;
; Handle Sigma or 1/Sigma user preference menu item.
if (event.id eq state.prefinv) then begin
	temp=widget_info(event.id,/button_set)
	state.preference[1]=temp
	widget_control,event.id,set_button=1-temp
	modify_output=1
endif
;
; The next two events trigger a new calculation.
; Handle 'incoherent scattering only' user preference menu item.
if (event.id eq state.prefinc) then begin
	temp=widget_info(event.id,/button_set)
	state.preference[2]=temp
	widget_control,event.id,set_button=1-temp
	docalculation=1
endif
;
; Handle the widget droplist containing formulas in the database.
if (event.id eq state.getformula_old) then begin
	state.formula=strtrim(event.str)
	widget_control,state.getformula_new,set_value=state.formula
	state.density=(*state.densitiesPtr)[event.index]
	state.packfrac=(*state.packfracsPtr)[event.index]
	widget_control,state.getdensity,set_value=string(state.density,format='(f8.3)')
	widget_control,state.getpackfrac,set_value=string(state.packfrac,format='(f8.3)')
	state.index=event.index
	docalculation=1
endif
;
; The next four events trigger a new calculation if the "Return" key was hit.
; Handle the widget where new formulas may be entered.
if (event.id eq state.getformula_new) then begin
	widget_control,state.getformula_new,get_value=temp
	state.formula=temp[0]
	if (event.type eq 0) then begin
		if (event.ch eq 10B) then docalculation=1
	endif
endif
;
; Handle the wavelength widget.
if (event.id eq state.getwavelength) then begin
	widget_control,state.getwavelength,get_value=temp
	state.wavelength=temp[0]
	if (event.type eq 0) then begin
		if (event.ch eq 10B) then begin
			widget_control,state.getwavelength,$
				set_value=string(temp[0],format='(f8.3)')
			docalculation=1
		endif
	endif
endif
;
; Handle the density widget.
if (event.id eq state.getdensity) then begin
	widget_control,state.getdensity,get_value=temp
	state.density=temp[0]
	if (event.type eq 0) then begin
		if (event.ch eq 10B) then begin
			widget_control,state.getdensity,$
				set_value=string(temp[0],format='(f8.3)')
			docalculation=1
		endif
	endif
endif
;
; Handle the packing fraction widget.
; If <CR> was hit, redo the calculation.
if (event.id eq state.getpackfrac) then begin
	widget_control,state.getpackfrac,get_value=temp
	state.packfrac=temp[0]
	if (event.type eq 0) then begin
		if (event.ch eq 10B) then begin
			widget_control,state.getpackfrac,$
				set_value=string(temp[0],format='(f8.3)')
			docalculation=1
		endif
	endif
endif
;
;
; If there is a "Show details" button and it has been pressed, respond.
if (state.details gt 0) then begin
	if (event.id eq state.details) then begin
		spw_displayresults,state.resultsPtr,state.formula,event
		widget_control,event.top,get_uvalue=state
	endif
endif
;
; This code takes care of deleting an entry in the database.
if (event.id eq state.delete) then begin
	widget_control,state.getformula_old,combobox_deleteitem=state.index
	formulae=*state.formulaePtr
	densities=*state.densitiesPtr
	packfracs=*state.packfracsPtr
	nvals=n_elements(formulae)
	if (nvals eq 1) then begin
		res=dialog_message("Cannot delete item; minimum number of entries is 1.")
		return
	endif
;
; Three possibilities exist, that the entry to be deleted is the first, the last, or in between.
; Handle these possibilities appropriately.
	case state.index of
		0: begin
			formulae=formulae[1:nvals-1]
			densities=densities[1:nvals-1]
			packfracs=packfracs[1:nvals-1]
		end
		nvals-1: begin
			formulae=formulae[0:nvals-2]
			densities=densities[0:nvals-2]
			packfracs=packfracs[0:nvals-2]
		end
		else: begin
			formulae=[formulae[0:state.index-1],formulae[state.index+1:nvals-1]]
			densities=[densities[0:state.index-1],densities[state.index+1:nvals-1]]
			packfracs=[packfracs[0:state.index-1],packfracs[state.index+1:nvals-1]]
		end
	endcase
	*state.formulaePtr=formulae
	*state.densitiesPtr=densities
	*state.packfracsPtr=packfracs
;
; Determine which entry to display instead of the one that was just deleted.
	if (n_elements(*state.formulaePtr)) eq state.index then state.index=state.index-1
	widget_control,state.getformula_old,set_combobox_select=state.index
	state.formula=(*state.formulaePtr)[state.index]
	widget_control,state.getformula_new,set_value=state.formula
	state.density=(*state.densitiesPtr)[state.index]
	widget_control,state.getdensity,set_value=string(state.density,format='(f8.3)')
	state.packfrac=(*state.packfracsPtr)[state.index]
	widget_control,state.getpackfrac,set_value=string(state.packfrac,format='(f8.3)')
	docalculation=1
endif
;
; React to the user preference menu item that determines whether or not to use the
;		number density calculator
if (event.id eq state.preflat) then begin
	this_event = tag_names(event,/structure_name)
;
; If this was a genuine button press, proceed as follows.
	if (this_event eq 'WIDGET_BUTTON') then begin
		temp=widget_info(event.id,/button_set)
		state.preference[3]=temp
;
; Decide course of action depending whether number density procedure is to be used.
		if (state.preference[3] eq 1) then begin
			widget_control,event.id,set_button=0
			widget_control,state.getdensity,sensitive=1
			widget_control,state.getdensity,set_value=string(state.density,format='(f8.3)')
			widget_control,state.shownumden,sensitive=0
		endif else begin
			widget_control,event.id,set_button=1
			widget_control,state.getdensity,sensitive=0
			widget_control,state.shownumden,sensitive=1
			widget_control,state.shownumden,set_value=string(state.rhon,format='(f8.5)')
			register_name = 'Number density calculator'
			group_leader = event.top
			notify_ids = [event.id,event.top]
;
;	Determine the id and tlb of the number density procedure's "send" button
			id=find_id(uname='numberdensity_sendit_button',tlb=top)
;
;	If there is no such procedure currently running, launch it.
;	If the procedure is currently running, send it the id and tlb of this widget (so that
; 	it will know where to send its answers).
			if (id eq 0l) then begin
				number_density,$
					group_leader = group_leader,$
					notify_ids = notify_ids,$
					register_name = register_name
			endif else begin
				widget_control,id,send_event={NEW_IDS_EVENT,id:id,top:top,handler:0l,notify_ids:notify_ids}
			endelse
			id=find_id(uname='numberdensity_sendit_button',tlb=top)
			widget_control,top,/show
		endelse
	endif
;
; If this is a pseudo-event from the number density widget, capture the appropriate item from the
;		answers array.
	if (this_event eq 'NUMDENCALC_EVENT') then begin
		rhon=event.answers[2]*1.e-24
		state.rhon=rhon
		widget_control,state.shownumden,set_value=string(rhon,format='(f8.5)')
		widget_control,event.top,set_uvalue=state
	endif
	docalculation=1
;
endif
;
; If the user preference for cm or mm or for Sigma or 1/Sigma has changed,
;		modify the output.
if (modify_output) then begin
	if (state.preference[0] eq 0 and state.preference[1] eq 0) then begin
		widget_control,state.labs,set_value='1/Scattering cross section (mm)  '
		widget_control,state.laba,set_value='1/Absorption cross section (mm)  '
		widget_control,state.labt,set_value='1/Total cross section (mm)       '
	endif
	if (state.preference[0] eq 1 and state.preference[1] eq 0) then begin
		widget_control,state.labs,set_value='1/Scattering cross section (cm)  '
		widget_control,state.laba,set_value='1/Absorption cross section (cm)  '
		widget_control,state.labt,set_value='1/Total cross section (cm)       '
	endif
	if (state.preference[0] eq 0 and state.preference[1] eq 1) then begin
		widget_control,state.labs,set_value='Scattering cross section (mm-1)  '
		widget_control,state.laba,set_value='Absorption cross section (mm-1)  '
		widget_control,state.labt,set_value='Total cross section (mm-1)       '
	endif
	if (state.preference[0] eq 1 and state.preference[1] eq 1) then begin
		widget_control,state.labs,set_value='Scattering cross section (cm-1)  '
		widget_control,state.laba,set_value='Absorption cross section (cm-1)  '
		widget_control,state.labt,set_value='Total cross section (cm-1)       '
	endif
	if (n_elements(*state.resultsPtr) gt 0) then begin
		tot_xssct=(*state.resultsPtr).tot_xssct
		tot_xsabs=(*state.resultsPtr).tot_xsabs
		rhonpf=(*state.resultsPtr).rhon
		if (state.preference[0] eq 0 and state.preference[1] eq 0) then begin
			widget_control,state.result[9],set_value=string(10./(tot_xssct*rhonpf))
			widget_control,state.result[10],set_value=string(10./(tot_xsabs*rhonpf))
			widget_control,state.result[11],set_value=string(10./((tot_xssct+tot_xsabs)*rhonpf))
		endif
		if (state.preference[0] eq 1 and state.preference[1] eq 0) then begin
			widget_control,state.result[9],set_value=string(1./(tot_xssct*rhonpf))
			widget_control,state.result[10],set_value=string(1./(tot_xsabs*rhonpf))
			widget_control,state.result[11],set_value=string(1./((tot_xssct+tot_xsabs)*rhonpf))
		endif
		if (state.preference[0] eq 0 and state.preference[1] eq 1) then begin
			widget_control,state.result[9],set_value=string(0.1*(tot_xssct*rhonpf))
			widget_control,state.result[10],set_value=string(0.1*(tot_xsabs*rhonpf))
			widget_control,state.result[11],set_value=string(0.1*((tot_xssct+tot_xsabs)*rhonpf))
		endif
		if (state.preference[0] eq 1 and state.preference[1] eq 1) then begin
			widget_control,state.result[9],set_value=string(tot_xssct*rhonpf)
			widget_control,state.result[10],set_value=string(tot_xsabs*rhonpf)
			widget_control,state.result[11],set_value=string((tot_xssct+tot_xsabs)*rhonpf)
		endif
	endif
endif
;
; Respond to button that saves database to file.
if (event.id eq state.dbsave) then begin
	res=file_test(state.file,/write)
	if (res eq 0) then begin
		res=dialog_message($
			["Formulae cannot be saved.","The file "+state.file+" is write protected."])
	endif else begin
		formulae=*state.formulaePtr
		densities=*state.densitiesPtr
		packfracs=*state.packfracsPtr
		save,file=state.file,formulae,densities,packfracs
	endelse
endif
;
widget_control,event.top,set_uvalue=state
;
if (docalculation) then sample_props_docalc,event
;
; These lines should be obvious.
if (event.id eq state.quit or event.id eq state.done) then begin
	widget_control,event.top,/destroy
	return
endif
;
end


;************************************************************************************************
function get_string_width,str,charsize = charsize
;************************************************************************************************
; (Written by RMD)
; INPUTS
;     STR:     string whose width in pixels we want
;
; OUTPUTS
;     WIDTH:   width in pixels of the string STR
if n_elements(charsize) eq 0 then charsize = 1
xsize = 300 & ysize = 300
window,/pixmap,/free,xsize = xsize,ysize = ysize
winpix = !d.window
xpos = 0 & ypos = 50
wset,winpix
xyouts,xpos,ypos,/device,str,charsize = charsize,width = this_width
width = fix(this_width*xsize)
wdelete,winpix
return,width   ; in pixels
end


;************************************************************************************************
pro sample_props_widget_table,event
;************************************************************************************************
;
compile_opt strictarr
;
;
register_name = 'cross_section_table'
group_leader = event.top
notify_ids = [event.id,event.top]
dave_cross_sections,$
	group_leader = group_leader,$
	notify_ids = notify_ids,$
	register_name = register_name
;
end


;************************************************************************************************
pro sample_props_widget_pdfr,event
;************************************************************************************************
;
compile_opt strictarr
;
;
void = define_vmPDFReader(tlb=event.top)
;
end


;************************************************************************************************
pro sample_props_widget_help,event
;************************************************************************************************
;
compile_opt strictarr
;
;
if (lmgr(/vm)) then begin
	file=sourceroot()+'xsecs_help.pdf'
	void = launch_vmhelp(file,tlb=event.top)
endif else begin
	file=!DAVE_AUXILIARY_DIR+'xsecs_help.pdf'
	void = launch_help(file,tlb=event.top)
endelse
;
end


;************************************************************************************************
pro sample_props_widget,$
   ; RMD added following 3 keywords to make this a non-modal widget
	group_leader = group_leader,$
	notify_ids = notify_ids,$
	register_name = register_name

;************************************************************************************************
;
compile_opt strictarr
;
;
;;;;;;;Added following 4 lines for non-modal widget action
if n_elements(group_leader) eq 0 then group_leader = 0L
if n_elements(notify_ids) eq 0 then notify_ids = [0L,0L]
if n_elements(register_name) eq 0 then register_name = 'sample_props_widget'
if xregistered(register_name) then return
;
; Retrieve database of information about chemical formulae.
if (lmgr(/vm)) then begin
	file=sourceroot()+"chemical_formulae.sav"
endif else begin
	file=!DAVE_AUXILIARY_DIR+'chemical_formulae.sav'
endelse
inputfile_exists=(file ne '')
;
if (inputfile_exists) then begin
	restore,file=file
	write_protected=file_test(file,/write) eq 0
endif else begin
	formulae='V'
	densities=5.96
	packfracs=1.0
	write_protected=1
endelse
;
formula=formulae[0]
density=densities[0]
packfrac=packfracs[0]
wavelength=1.798
rhon=0.0
avonum=0.60221415
;
;preference[0]=0 for mm, 1 for cm.
;preference[1]=0 for 1/Sigma, 1 for Sigma.
;preference[2]=0 to use incoherent scattering only, else 1.
;preference[3]=0 to calculate number density from lattice parameters, else 1.
preference=[1,1,1,1]
;
; Start to define the main widget.
tlbtitle='Molecular weight, number density, and scattering and absorption cross sections'
tlb=widget_base(title=tlbtitle,/row,group_leader=group_leader,mbar=bar,$
	uname="sample_props_widget_tlb")
	menu=widget_button(bar,/menu,value="Options")
		prefmm=widget_button(menu,value='Express Sigma (1/Sigma) in mm (mm-1)',/checked_menu)
		prefinv=widget_button(menu,value='Show 1/Sigma values',/checked_menu)
		prefinc=widget_button(menu,value='Only include incoherent scattering (exclude coherent)',/checked_menu)
		preflat=widget_button(menu,value='Calculate number density from lattice parameters',/checked_menu)
		done=widget_button(menu,value="Quit")
	hmen=widget_button(bar,/menu,value="Information")
		void=widget_button(hmen,value="Program description",event_pro='sample_props_widget_help')
		void=widget_button(hmen,value="Table of cross sections",event_pro='sample_props_widget_table')
	pmen=widget_button(bar,/menu,value="pdf reader",sensitive=lmgr(/vm))
		void=widget_button(pmen,value="Define pdf reader",event_pro='sample_props_widget_pdfr')
	base1=widget_base(tlb,/col)
		base1a=widget_base(base1,/col,/frame,space=6)
			base1a1=widget_base(base1a,/col)
				label_old=widget_label(base1a1,/align_left,value='Select a chemical formula....')
				getformula_old=widget_combobox(base1a1,value=formulae,xsize=30,/dynamic_resize)
				label_new=widget_label(base1a1,/align_left,value='or type in a new formula')
				getformula_new=widget_text(base1a1,/editable,/all_events,value=formula,xsize=30)
			delete=widget_button(base1a,value='Remove selected item from list')
		base1b=widget_base(base1,/col,/frame)
			base1b1=widget_base(base1b,/row)
				base1b1a=widget_base(base1b1,/col)
					void=widget_label(base1b1a,/align_left,value='Density (g/cc)')
					getdensity=widget_text(base1b1a,xsize=13,$
						value=string(density,format='(f8.3)'),/all_events,/editable)
				base1b1b=widget_base(base1b1,/col)
					void=widget_label(base1b1b,/align_left,value='Number density')
					shownumden=widget_text(base1b1b,xsize=13,/editable,sensitive=0)
			base1b2=widget_base(base1b,/row)
				base1b2a=widget_base(base1b2,/col)
					void=widget_label(base1b2a,/align_left,value='Packing fraction')
					getpackfrac=widget_text(base1b2a,xsize=13,$
						value=string(packfrac,format='(f8.3)'),/all_events,/editable)
				base1b2b=widget_base(base1b2,/col)
					void=widget_label(base1b2b,/align_left,value='Wavelength (A)')
					getwavelength=widget_text(base1b2b,xsize=13,$
						value=string(wavelength,format='(f8.3)'),/all_events,/editable)
		docalc=widget_button(base1,value='Calculate cross sections',$
			event_pro='sample_props_docalc')
		dbsave=widget_button(base1,value='Save formulae, densities etc',sensitive=1-write_protected)
		dbrestore=widget_button(base1,value='Restore formulae, densities etc',$
			event_pro='sample_props_restore')
		quit=widget_button(base1,value='Exit',$
			tooltip='Exit without saving formulae, densities etc')
;
	ssize=get_screen_size()
	xsize=ssize[0]*0.5
	frac=0.3
	base2=widget_base(tlb,/col,scr_xsize=xsize)
		base2a=widget_base(base2,/row,/frame,scr_xsize=xsize)
			base2a1=widget_base(base2a,/col,scr_xsize=xsize*frac)
			base2a2=widget_base(base2a,/col,scr_xsize=xsize*(0.5-frac))
			base2a3=widget_base(base2a,/col,scr_xsize=xsize*frac)
			base2a4=widget_base(base2a,/col,scr_xsize=xsize*(0.5*frac))
;
			result=lonarr(12)
			void=widget_label(base2a1,value='Molecular weight',/align_left)
			void=widget_label(base2a1,value='Number density (10^24/cm3)',/align_left)
			void=widget_label(base2a1,value='Mass density (g/cm3)',/align_left)
			void=widget_label(base2a1,value='Scattering cross section (b)',/align_left)
			void=widget_label(base2a1,value='Absorption cross section (b)',/align_left)
			void=widget_label(base2a1,value='Total cross section (b)',/align_left)
			for k=0,5 do result[k]=widget_label(base2a2,value=' ',/align_left,/dynamic_resize)
;
			void=widget_label(base2a3,value='Wavelength (A)',/align_left)
			void=widget_label(base2a3,value='Packed number density (10^24/cm3)',/align_left)
			void=widget_label(base2a3,value='Packed mass density (g/cm3)',/align_left)
			if (preference[0] eq 0 and preference[1] eq 0) then begin
				labs=widget_label(base2a3,value='1/Scattering cross section (mm)  ',/align_left)
				laba=widget_label(base2a3,value='1/Absorption cross section (mm)  ',/align_left)
				labt=widget_label(base2a3,value='1/Total cross section (mm)       ',/align_left)
			endif
			if (preference[0] eq 1 and preference[1] eq 0) then begin
				labs=widget_label(base2a3,value='1/Scattering cross section (cm)  ',/align_left)
				laba=widget_label(base2a3,value='1/Absorption cross section (cm)  ',/align_left)
				labt=widget_label(base2a3,value='1/Total cross section (cm)       ',/align_left)
			endif
			if (preference[0] eq 0 and preference[1] eq 1) then begin
				labs=widget_label(base2a3,value='Scattering cross section (mm-1)  ',/align_left)
				laba=widget_label(base2a3,value='Absorption cross section (mm-1)  ',/align_left)
				labt=widget_label(base2a3,value='Total cross section (mm-1)       ',/align_left)
			endif
			if (preference[0] eq 1 and preference[1] eq 1) then begin
				labs=widget_label(base2a3,value='Scattering cross section (cm-1)  ',/align_left)
				laba=widget_label(base2a3,value='Absorption cross section (cm-1)  ',/align_left)
				labt=widget_label(base2a3,value='Total cross section (cm-1)       ',/align_left)
			endif
			for k=6,11 do result[k]=widget_label(base2a4,value=' ',/align_left,/dynamic_resize)
;
centertlb,tlb
geom=widget_info(getformula_new,/geometry)
widget_control,getformula_old,scr_xsize=geom.scr_xsize
widget_control,tlb,/realize
;
formulaePtr=ptr_new(formulae)
densitiesPtr=ptr_new(densities)
packfracsPtr=ptr_new(packfracs)
resultsPtr=ptr_new(/allocate_heap)
xsecsPtr=ptr_new(/allocate_heap)
;
state={$
	avonum:avonum,$
	tlbtitle:tlbtitle,$
	getformula_old:getformula_old,$
	getformula_new:getformula_new,$
	getdensity:getdensity,$
	shownumden:shownumden,$
	getpackfrac:getpackfrac,$
	getwavelength:getwavelength,$
	delete:delete,$
	docalc:docalc,$
	prefmm:prefmm,$
	prefinv:prefinv,$
	prefinc:prefinc,$
	preflat:preflat,$
	dbsave:dbsave,$
	dbrestore:dbrestore,$
	quit:quit,$
	done:done,$
	formulaePtr:formulaePtr,$
	densitiesPtr:densitiesPtr,$
	packfracsPtr:packfracsPtr,$
	formula:formula,$
	density:density,$
	rhon:rhon,$
	packfrac:packfrac,$
	wavelength:wavelength,$
	preference:preference,$
	file:file,$
	index:0,$
	result:result,$
	base2:base2,$
	labs:labs,$
	laba:laba,$
	labt:labt,$
	details:0l,$
	sorted:0,$
	notify_ids:notify_ids,$  ; added for non-modal widget
	resultsPtr:resultsPtr,$
	xsecsPtr:xsecsPtr}
;
widget_control,tlb,set_uvalue=state
;
; Do the initial calculation.
sample_props_docalc,{id:docalc,top:tlb,handler:0l}
;
; RMD added register_name variable to the call to xmanager.
xmanager,register_name,tlb,/no_block,cleanup='sample_props_widget_cleanup',$
	event_handler='sample_props_widget_event'
;
end
