; $Id: fans_plotdata.pro,v 1.16 2007/10/12 22:55:44 azuah Exp $
;###############################################################################
;
; NAME:
;   fans_plotdata
;
; PURPOSE:
;   Utility routines for FANS data reduction application defined in
;   the file fans_datreduc.pro. The routines in this file deal with
;   plotting data in the various graphic windows.
;
; CATEGORY:
;   FANS Data Reduction
;
; AUTHOR:
;   Richard Tumanjong Azuah
;   NIST Center for Neutron Research
;   100 Bureau Drive, Gaithersburg, MD 20899
;   United States
;   azuah@nist.gov; (301) 9755604
;   May, 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 of if the code in this file is
;  included in another product.
;
;###############################################################################



;===============================================================================
; fans_updMskList
;
; PURPOSE:
;   Maintains a list of detectors that are to be masked from the final
;   reduced data.
;
; PARAMETERS:
;   sPtr [in] - pointer to state structure
;
;   detnos [in] - detector number whose state has changed masked<->unmasked
;
; KEYWORDS:
;   err [out] - err number, if an error occured
;
;   emsg [out] - explanatory error message, if an error occured
;
pro fans_updMskList, sPtr, detnos, err=err,emsg=emsg
err=0 & emsg=''

; one input parameters necessary
if (n_params() ne 2) then begin
    err = 1
    emsg = 'In "fans_updMskList" - Invalid number of parameters'
    return
endif

; check input parameters are defined
if ( (n_elements(detnos) le 0)  or (ptr_valid(sPtr) eq 0) ) then begin
    err = 2
    emsg = 'In "fans_updMskList" - Input parameter(s) undefined'
    return
endif

; grap the current mask list
if  ptr_valid((*sPtr).mskListPtr) then list = (*(*sPtr).mskListPtr)
ptr_free,(*sPtr).mskListPtr     ; clean (empty) the current list

lsize = n_elements(list)
if (lsize gt 0) then begin      ; list exists
    result = where(list eq detnos)
    if (result[0] ge 0) then begin ; detnos is in the list already - remove it
        lsize = lsize - 1
        if (result[0] le lsize-1) then $
          for i = result[0], lsize-1 do list[i] = list[i+1]
    endif else begin            ; detnos not in list - add it
        lsize = lsize + 1
        list = [list,detnos]
    endelse
endif else begin                ; no list yet - create one
    lsize = 1
    list = [detnos]
endelse 

if (lsize ge (*(*(*sPtr).sDataPtr)[(*sPtr).sampIndex]).ndets-2) then begin ; removing all detectors!!!
    message = ['Attempting to mask all detectors.', $
               'No output can be generated!']
    void = dialog_message(message,/information,dialog_parent=(*sPtr).tlbID)
endif


; update state variable
lstring = ''
; mskOnID should be desensitized and off (ie deselected) by default unless there
; are actually detectors to be masked.
widget_control, (*sPtr).mskOnID, get_uvalue=mskon ; get current msk status (ie whether on/off)
widget_control, (*sPtr).mskOnID, sensitive=0 ; should be desensitized
if (mskon) then $               ; if on, switch it off! but remember it was on
  widget_control, (*sPtr).mskOnID, set_button=0, set_uvalue=0

if (lsize gt 0) then begin
    widget_control, (*sPtr).mskOnID, sensitive=1
    if (mskon) then $           ; if it was on previously, switch it back on
      widget_control, (*sPtr).mskOnID, set_button=1, set_uvalue=mskon
    newlist = list[0:lsize-1]
    newlist = newlist[sort(newlist)] ; sort in ascending order
    (*sPtr).mskListPtr = ptr_new(newlist)
    if (lsize eq 1) then begin
      lstring =   strtrim(string(newlist[0]),2)
    endif else begin
        beg = newlist[0]
        range = 0
        for i = 1,lsize-1 do begin
            if (newlist[i] eq newlist[i-1]+1) then begin
                range=1
                continue
            endif
            if (range) then begin
                lstring = lstring+strtrim(string(beg),2)+'-'+strtrim(string(newlist[i-1]),2)+' '
                beg = newlist[i]
                range = 0
            endif else begin
                lstring = lstring+strtrim(string(newlist[i-1]),2)+' '
                beg = newlist[i]
            endelse
        endfor
        if (range) then begin
            lstring = lstring+strtrim(string(beg),2)+'-'+strtrim(string(newlist[i-1]),2)+' '
        endif else begin
            lstring = lstring+strtrim(string(newlist[i-1]),2)+' '
        endelse
    endelse  
;    strarr = strtrim(string(newlist),1) 
;    for i = 0,lsize-1 do $
;      lstring = lstring + strarr[i] + ' '
endif 

; switch on output calculation flag
(*sPtr).doOutpCalc = 1

; indicates Fast bkgd fit has to be recalculated
(*sPtr).updFast = 1


; update list in mask widget
widget_control, (*sPtr).mskListID, set_value=lstring

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



;===============================================================================
; fans_plotMskList
;
; PURPOSE:
;   Mark all masked detectors with grey bars in the display window.
;
; PARAMETERS:
;   sPtr [in] - pointer to state structure
;
; KEYWORDS:
;   err [out] - err number, if an error occured
;
;   emsg [out] - explanatory error message, if an error occured
;
pro fans_plotMskList, sPtr, err=err, emsg=emsg
err=0 & emsg=''

; one input parameters necessary
if (n_params() ne 1) then begin
    err = 1
    emsg = 'In "fans_plotMskList" - Invalid number of parameters'
    return
endif

; check input parameters are defined
if (ptr_valid(sPtr) eq 0) then begin
    err = 2
    emsg = 'In "fans_plotMskList" - Input parameter undefined'
    return
endif

if ( ptr_valid((*sPtr).mskListPtr) eq 0) then return

list = (*(*sPtr).mskListPtr)

lsize = n_elements(list)
ymin = !y.crange[0]  &  ymax = !y.crange[1]
yvals = [ymin,ymin,ymax,ymax]
for i = 0, lsize-1 do begin
    xmin = list[i]-0.50  &  xmax=list[i]+0.50
    xvals = [xmin,xmax,xmax,xmin]
    polyfill,xvals,yvals
endfor


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



;===============================================================================
; fans_plotClick
;
; PURPOSE:
;   Event handler for button events dispatched from the plot
;   window. When detector display mode is on (ie samp, bkgd views)
;   then deal with left-mouse clicks to mask/unmask dets else
;   zoom/reset display. Mark all masked detectors with grey bars in
;   the display window.
;
; PARAMETERS:
;   event [in] - event structure
;
pro fans_plotClick, event

widget_control, event.top, get_uvalue=sPtr

; determine current view mode
widget_control, (*sPtr).sViewID, get_uvalue=svOn
widget_control, (*sPtr).bViewID, get_uvalue=bvOn
widget_control, (*sPtr).fViewID, get_uvalue=fvOn
widget_control, (*sPtr).oViewID, get_uvalue=ovOn

if svOn then dataType = 'samp'
if bvOn then dataType = 'bkgd'
if fvOn then dataType = 'fast'
if ovOn then dataType = 'outp'

if ( (dataType eq 'samp') or (dataType eq 'bkgd') ) then begin ; mask/unmask dets

    
    if (event.press eq 1) then begin ; left mb press
        (*sPtr).zp1 = [event.x,event.y] ; record mouse location
        (*sPtr).mouseType = event.press ; need to know afterwards that a left mb was pressed
        
        ;; draw a vertical line at the mouse location
        ymin = !y.crange[0]  &  ymax = !y.crange[1]
        xydat = convert_coord(event.x,event.y,/device,/to_data)
        plots, [xydat[0],xydat[0]], [ymin,ymax], color=(*sPtr).colors.red, /data
        (*sPtr).zp2 = [-1000.0,-1.0]   ; used to determine if motion event occurs b/c zp2 will be different
    endif 

    ;; if motion event and left mb was just pressed
    if ((event.type eq 2) and ((*sPtr).mouseType eq 1) ) then begin
        (*sPtr).zp2 = [event.x, event.y]
        zp1 = (*sPtr).zp1
        zp2 = (*sPtr).zp2
        ;; copy winPix to winVis and draw vertical lines at current mouse 
        ;; location and where left mb was initially pressed 
        device, copy=[0,0,(*sPtr).winWidth,(*sPtr).winHeight,0,0,(*sPtr).winPix]
        ymin = !y.crange[0]  &  ymax = !y.crange[1]
        xydat = convert_coord(zp1[0],zp1[1],/device,/to_data)
        plots, [xydat[0],xydat[0]], [ymin,ymax], color=(*sPtr).colors.red, /data
        xydat = convert_coord(zp2[0],zp2[1],/device,/to_data)
        plots, [xydat[0],xydat[0]], [ymin,ymax], color=(*sPtr).colors.red, /data
    endif

    if (event.release eq 1) then begin ; left mb released

        lolim=2.5
        hilim=50.49
        if (ptr_valid((*sPtr).sDataPtr)) then begin
            hilim = (*(*(*sPtr).sDataPtr)[0]).ndets + 0.49
        endif

        if ( ((*sPtr).zp2)[0] ne -1000.0) then begin
            zp1 = (*sPtr).zp1   ; initial xvalue for left mouse press
            zp2 = (*sPtr).zp2   ; final mouse xvalue after motion event
            x1 = min([zp1[0],zp2[0]],max=x2)
            y = zp1[1]
            d1 = 0
            d2 = 0
            ;; if the x coord of mouse occurs outside the detector range, ignore it
            xydat = convert_coord(x1,y,/device,/to_data)
            d1 = round(xydat[0])
            xydat = convert_coord(x2,y,/device,/to_data)
            d2 = round(xydat[0])
            for detnos = d1,d2 do begin
                if ( (detnos lt lolim) or (detnos gt hilim) ) then continue
                
                ;; add/remove det from mask list if it isn't/is there followed by an
                ;; update to the mask widget
                fans_updMskList, sPtr,detnos
            endfor 
        endif else begin
            zp1 = (*sPtr).zp1   ; initial xvalue for left mouse press
            x1 = zp1[0]
            y = zp1[1]
            xydat = convert_coord(x1,y,/device,/to_data)
            detnos = round(xydat[0])
            ;; add/remove det from mask list if it isn't/is there followed by an
            ;; update to the mask widget
            if ( (detnos ge lolim) and (detnos le hilim) ) then fans_updMskList, sPtr,detnos
        endelse 
        ;; refresh plot window 
        fans_plotData, sPtr, dataType, err=err,emsg=emsg
        if (err ne 0) then begin
            printerr,err,emsg
            return
        endif
        (*sPtr).zp2 = [0.0,0.0]
        (*sPtr).mouseType = 0   ; reset mouseType as prev press been dealt with
    endif 
    
endif else begin                ; zoom/reset plot

    case event.type of
        0: begin                ; button press
            (*sPtr).mouseType = event.press
            if ((*sPtr).mouseType eq 4) then begin ; right-hand mb so reset plot to full range
                fans_plotData, sPtr, dataType, err=err, emsg=emsg
                if (err ne 0) then begin
                    printerr,err,emsg
                    return
                endif
                (*sPtr).mouseType = 0 ; set mouse type to undefined value
                return
            endif
            if ((*sPtr).mouseType eq 1) then begin ; left-hand mb so record mouse location
                (*sPtr).zp1 = [event.x,event.y]
            endif
        end
        1: begin                ; button release
            if ((*sPtr).mouseType eq 1) then begin ; mouse press was a left-hand mb
                zp1 = (*sPtr).zp1
                zp2 = (*sPtr).zp2
                x = min([zp1[0], zp2[0]])
                y = min([zp1[1], zp2[1]])
                w = abs(zp1[0] - zp2[0])
                h = abs(zp1[1] - zp2[1])
                x2 = x+w
                y2 = y+h
                lc = convert_coord(x, y, /device, /to_data)
                uc = convert_coord(x2, y2, /device, /to_data)
                fans_plotData, sPtr, dataType,xrange=[lc[0],uc[0]], yrange=[lc[1],uc[1]],err=err,emsg=emsg
                if (err ne 0) then begin
                    printerr,err,emsg
                    return
                endif
                if (err ne 0) then begin
                    printerr,err,emsg
                    return
                endif
                (*sPtr).zp2 = [0.0,0.0] ; reset to zero
            endif
            (*sPtr).mouseType = 0
        end
        2: begin                ; mouse motion
            if ((*sPtr).mouseType eq 1) then begin ; mouse press was a left-hand mb
                (*sPtr).zp2 = [event.x, event.y]
                zp1 = (*sPtr).zp1
                zp2 = (*sPtr).zp2
                xc = [zp1[0], event.x, event.x, zp1[0], zp1[0]]
                yc = [zp1[1], zp1[1], event.y, event.y, zp1[1]]
                device, copy=[0,0,(*sPtr).winWidth,(*sPtr).winHeight,0,0,(*sPtr).winPix] ; copy contents of winPix to winVis
                plots, xc, yc, color=(*sPtr).colors.red, /device
            endif
        end
        else:
    endcase    
endelse                         ; zoom/reset



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



;===============================================================================
; fans_locateIndex
;
; PURPOSE:
;   Given a 1D 'array', this function returns the index of the element
;   which is closest to the input 'val'.
;
; PARAMETERS:
;   array [in] - the input array
;
;   val [in] - the value to be located in the array
;
; RETURN VALUE:
;   Index of array value closest to val.
;
function locateIndex, array,val

nspec = size(array,/n_elements)
result = where(array gt val)
if (result[0] lt 0) then begin  ; ie if where function returns null result
    index = nspec-1             ; ==> use last index
endif else begin
    index = result[0]
    diff1 = abs(val - array[index-1]) 
    diff2 = abs(array[index] - val)
    if (diff1 le diff2) then index = index - 1 ; use index of element closer to val!
endelse

return, index

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



;===============================================================================
; fans_setSliderValue
;
; PURPOSE:
;   Slider values are continuous. However, the energy values that the
;   slider represents only have certain values given by the energy
;   array. This routine ensures that the slider only sets/displays
;   valid energy values that are present in the energy array. This
;   routine must be called whenever the data being displayed changes.
;
; PARAMETERS:
;   wid [in] - widget id of slider
;
;   energyPtr [in] - pointer to energy transfer array
;
;   dvalue [in] - the energy value initially selected using the
;                 slider. This is used to locate the closest valid
;                 energy.
;
; KEYWORDS:
;   err [out] - err number, if an error occured
;
;   emsg [out] - explanatory error message, if an error occured
;
pro fans_setSliderValue, wid, energyPtr, dvalue, err=err, emsg=emsg

err=0  &  emsg = ''

; one input parameters necessary
if (n_params() lt 2) then begin
    err = 1
    emsg = 'In "fans_setSliderValue" - Invalid number of parameters'
    return
endif

; check input parameters are defined
if ( (n_elements(wid) le 0)  or (ptr_valid(energyPtr) eq 0) ) then begin
    err = 2
    emsg = 'In "fans_setSliderValue" - Input parameter(s) undefined'
    return
endif

; if input parameter dvalue missing, use current slider value instead
if (n_elements(dvalue) le 0) then $
  widget_control, wid, get_value=dvalue ;dvalue = fslider_get_value(wid) 

; now locate the closest entry to dvalue in the energy array
index = locateIndex((*energyPtr),dvalue)

; update slider value with this 'proper' energy 
widget_control, wid, set_value=(*energyPtr)[index]

; save this index in the uvalue of the widget
widget_control, wid, set_uvalue=index

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



;===============================================================================
; fans_syncSlider
;
; PURPOSE:
;   Ensure that the energy range displayed in the slider matches the
;   currently selected dataset.
;
; PARAMETERS:
;   wid [in] - widget id of slider
;
;   energyPtr [in] - pointer to energy transfer array
;
;   reset [in] - flag. If set, the slider value is set to its minimum.
;
; KEYWORDS:
;   err [out] - err number, if an error occured
;
;   emsg [out] - explanatory error message, if an error occured
;
pro fans_syncSlider, wid, energyPtr, reset, err=err, emsg=emsg

err=0  &  emsg = ''
; two input parameters necessary
if (n_params() ne 3) then begin
    err = 1
    emsg = 'In "syncSlider" - Invalid number of procedure parameters'
    return
endif

; check input parameters are defined
if ((n_elements(wid) le 0)or(ptr_valid(energyPtr) eq 0)or(n_elements(reset) eq 0)) then begin
    err = 2
    emsg = 'In "syncSlider" - Input parameter(s) undefined'
    return
endif

info = size(*energyPtr)
if (info[0] ne 1) then begin
    err = 3
    emsg = 'In "syncSlider" - Input parameter has wrong dimension'
    return
endif

dmin = min(*energyPtr, max=dmax)
if (reset gt 0) then begin
    ;; set slider range to be same as energy array. Initialise it to its
    ;; min value
    range = [dmin,dmin,dmax]
    widget_control, wid, set_value = range
    fans_setSliderValue, wid, energyPtr, dmin, err=err, emsg=emsg
endif else begin
    widget_control, wid, get_value=dvalue ;dvalue = fslider_get_value(wid)
    if (dvalue lt dmin) then dvalue = dmin
    if (dvalue gt dmax) then dvalue = dmax
    range = [dvalue,dmin,dmax]
    widget_control, wid, set_value = range
    fans_setSliderValue, wid, energyPtr, davlue, err=err,emsg=emsg
endelse

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



;===============================================================================
; fans_sliderEvent
;
; PURPOSE:
;   Event handler: for energy cut slider widget.
;
; PARAMETERS:
;   event [in] - event structure.
;
pro fans_sliderEvent, event

widget_control,event.top,get_uvalue=sPtr

; determine which type of data is being displayed. Note that it only
; make sense to view energy slices for sample and bkgd data. Cuts for
; Fast bkgd and output data are not implemented

widget_control, (*sPtr).sViewID, get_uvalue=svOn
widget_control, (*sPtr).bViewID, get_uvalue=bvOn
;widget_control, (*sPtr).oViewID, get_uvalue=ovOn

if svOn then dataType = 'samp'
if bvOn then dataType = 'bkgd'
;if ovOn then dataType = 'outp'

if ( n_elements(dataType) le 0) then begin
    if (!debug) then print,'Improper behaviour within "fans_sliderEvent"'
    return
endif

; get the energy array of current data
switch dataType of

    'samp': begin
        index = (*sPtr).sampIndex
        fdStructPtr = (*(*sPtr).sDataPtr)[index] ; get a ptr to the approp. fans data struct
        break
    end

    'bkgd': begin
        index = (*sPtr).bkgdIndex
        fdStructPtr = (*(*sPtr).bDataPtr)[index] ; get a ptr to the approp. fans data struct
        break
    end

;    'outp': begin
;        print,'To be implemented...'
;        return
;        index = (*sPtr).outpIndex
;        fdStructPtr = (*(*sPtr).oDataPtr)[index] ; get a ptr to the approp. fans data struct
;        break
;    end


endswitch

energyPtr = (*fdStructPtr).energyPtr

; now alter the slider value so it is a valid value within the energy array.
fans_setSliderValue,(*sPtr).eSliderID,energyPtr,err=err,emsg=emsg
if (err ne 0) then begin
    printerror,err,emsg
    return
endif

; finally, update the plot.
fans_plotdata,sPtr,dataType

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



;===============================================================================
; fans_sumData
;
; PURPOSE:
;   Plot data in the draw widget.
;
; PARAMETERS:
;   sPtr [in] - pointer to state structure
;
;   dataType [in] - data type string: 'samp'|'bkgd'|'fast'|'outp'
;
;   xrange [in] - range of x values to be displayed
;
;   yrange [in] - range of y values to be displayed
;
; KEYWORDS:
;   err [out] - err number, if an error occured
;
;   emsg [out] - explanatory error message, if an error occured
;



;===============================================================================
pro fans_plotData, sPtr, dataType, xrange=xrge, yrange=yrge, err=err,emsg=emsg

err = 0  &  emsg = ''

; two input parameters necessary
if (n_params() ne 2) then begin
    err = 1
    emsg = '"In fans_plotData" - Invalid number of procedure parameters'
    return
endif;===============================================================================


; check input parameters are defined
if ( (n_elements(dataType) le 0)  or (ptr_valid(sPtr) eq 0) ) then begin
    err = 2
    emsg = 'In "fans_plotData" - Input parameter(s) undefined'
    return
endif


switch dataType of

    'samp': begin
        index = (*sPtr).sampIndex
        fdStructPtr = (*(*sPtr).sDataPtr)[index] ; get a ptr to the approp. fans data struct
        date = (*fdStructPtr).date
        title = (*fdStructPtr).title
        subtitle = 'Expt date: '+date
        xtitle = 'Detector number'
        ;; determine appropriate label based on scan type.
        switch (*fdStructPtr).base of
            'TIME': begin
                ftime = fix((*(*fdStructPtr).timePtr)[0] * 60)
                ytitle = 'Counts per '+strtrim(string(ftime),2)+' secs'
                break
            end
            
            'NEUT':
            else: begin
                monCnts = (*fdStructPtr).monitor * (*fdStructPtr).prefactor
                ytitle = 'Counts per '+strtrim(string(monCnts),2)+' mon'
                break
            end
        endswitch        
        ;; get index of energy value set by slider
        widget_control, (*sPtr).eSliderID, get_uvalue=eIndex
        
        ;; get detector counts for this energy
        ;; NB dets 1 and 2 are monitors!
        ndets = (*fdStructPtr).ndets
        nspec = (*fdStructPtr).nspec
        if ( (eIndex lt 0) or (eIndex ge nspec) ) then eIndex=0 
        detCnts = (*(*fdStructPtr).detCntsPtr)[2:ndets-1,eIndex] ; ignore monitors
        detNos = indgen(ndets-2)+3

        ;; set winPix as destination window initially
        wset, (*sPtr).winPix
        
        ;; plot detector counts vs detector number
        plot,detNos,detCnts, $
             xstyle=1,/ynozero,psym=-4,symsize=1.5,linestyle=0,charsize=1.4, $
             xtitle=xtitle,subtitle=subtitle,ytitle=ytitle,title=title, $
             xrange=[0,ndets+2]
        ;; plot errors
            sigma = (*(*fdStructPtr).detErrsPtr)[2:ndets-1,eIndex] 
            errplot, detNos, detCnts-sigma, detCnts+sigma ; plot errors

        ;; display masked detectors
        fans_plotMskList, sPtr

        wset, (*sPtr).winVis    ; set winVis as destination window
        device, copy=[0,0,(*sPtr).winWidth,(*sPtr).winHeight,0,0,(*sPtr).winPix] ; copy contents of winPix to winVis

        break
    end

    'bkgd': begin
        index = (*sPtr).bkgdIndex
        fdStructPtr = (*(*sPtr).bDataPtr)[index] ; get a ptr to the approp. fans data struct
        date = (*fdStructPtr).date
        title = (*fdStructPtr).title
        subtitle = 'Expt date: '+date
        xtitle = 'Detector number'
        ;; determine appropriate label based on scan type.
        switch (*fdStructPtr).base of
            'TIME': begin
                ftime = fix((*(*fdStructPtr).timePtr)[0] * 60)
                ytitle = 'Counts per '+strtrim(string(ftime),2)+' secs'
                break
            end
            
            'NEUT':
            else: begin
                monCnts = (*fdStructPtr).monitor * (*fdStructPtr).prefactor
                ytitle = 'Counts per '+strtrim(string(monCnts),2)+' mon'
                break
            end
        endswitch        

        ;; get index of energy value set by slider
        widget_control, (*sPtr).eSliderID, get_uvalue=eIndex
        
        ;; get detector counts for this energy
        ;; NB dets 1 and 2 are monitors!
        ndets = (*fdStructPtr).ndets
        nspec = (*fdStructPtr).nspec
        if ( (eIndex lt 0) or (eIndex ge nspec) ) then eIndex=0 
        detCnts = (*(*fdStructPtr).detCntsPtr)[2:ndets-1,eIndex] ; ignore monitors
        detNos = indgen(ndets-2)+3
        
        ;; set winPix as destination window initially
        wset, (*sPtr).winPix

        ;; plot detector counts vs detector number
        plot,detNos,detCnts, $
             xstyle=1,/ynozero,psym=-4,symsize=1.5,linestyle=0,charsize=1.4, $
             xtitle=xtitle,subtitle=subtitle,ytitle=ytitle,title=title, $
             xrange=[0,ndets+2]
        ;; plot errors
            sigma = (*(*fdStructPtr).detErrsPtr)[2:ndets-1,eIndex] ;0.5*sqrt(detCnts)
            errplot, detNos, detCnts-sigma, detCnts+sigma ; plot errors

        ;; display masked detectors
        fans_plotMskList, sPtr

        wset, (*sPtr).winVis    ; set winVis as destination window
        device, copy=[0,0,(*sPtr).winWidth,(*sPtr).winHeight,0,0,(*sPtr).winPix] ; copy contents of winPix to winVis
        
        break
    end

    'fast': begin
        index = (*sPtr).fastIndex
        fdStructPtr = (*(*sPtr).fDataPtr)[index] ; get a ptr to the approp. fans data struct
        omega = (*(*fdStructPtr).energyPtr) ; energy tranfers
        ;dspace = (*fdStructPtr).monDSpace ; get the monochromator d-space value
        widget_control, (*sPtr).mskOnID, get_uvalue=mskon
        if (mskon) then begin 
            goodDets = -1
            goodDets = fans_maskDets(sPtr) ; get a list of good dets
            if ((ptr_valid((*sPtr).mskListPtr) eq 1) or goodDets[0] ne -1) then begin
                nspec = (*fdStructPtr).nspec
                totCnts = (sigma = fltarr(nspec))
                ;; in exps below, gooddets refer to the dets so need to sub 1
                ;; to get correct indices
                for i = 0,nspec-1 do begin ; re-calculate total cnts using only good dets
                    totCnts[i] = total((*(*fdStructPtr).detCntsPtr)[goodDets-1,i])
                    sigma[i] = sqrt(total(((*(*fdStructPtr).detErrsPtr)[goodDets-1,i])^2))
                endfor
            endif
        endif else begin
            totCnts = (*(*fdStructPtr).totCntsPtr) ; total counts
            sigma = (*(*fdStructPtr).totErrsPtr)
        endelse
        date = (*fdStructPtr).date
        title = (*fdStructPtr).title
        subtitle = 'Expt date: '+date
        unit_str = ((*(*sPtr).mfoPtr)[3] eq 0)? '(meV)' : '(!ncm!u-1!n)'
        xtitle = 'Energy Transfer '+unit_str
        ;; determine appropriate label based on scan type.
        switch (*fdStructPtr).base of
            'TIME': begin
                ftime = fix((*(*fdStructPtr).timePtr)[0] * 60)
                ytitle = 'Counts per '+strtrim(string(ftime),2)+' secs'
                break
            end
            
            'NEUT':
            else: begin
                monCnts = (*fdStructPtr).monitor * (*fdStructPtr).prefactor
                ytitle = 'Counts per '+strtrim(string(monCnts),2)+' mon'
                break
            end
        endswitch        
        
        ;; set winPix as destination window initially
        wset, (*sPtr).winPix

        if ( (n_elements(xrge) eq 0) or (n_elements(yrge) eq 0) ) then begin
            plot,omega,totCnts, $
              xstyle=1,ystyle=1,psym=-4,symsize=1.5,linestyle=0,charsize=1.4, $
              xtitle=xtitle,subtitle=subtitle,ytitle=ytitle,title=title
        endif else begin 
            plot,omega,totCnts, $
              xrange=xrge,yrange=yrge, $
              xstyle=1,ystyle=1,psym=-4,symsize=1.5,linestyle=0,charsize=1.4, $
              xtitle=xtitle,subtitle=subtitle,ytitle=ytitle,title=title
        endelse 
        ;; plot errors
        errplot, omega, totCnts-sigma, totCnts+sigma ; plot errors

        ;; plot fit, if available
        ;; retrieve parameters from the text widget
        ;widget_control, (*sPtr).parID, get_value=parstr
        ;if (n_elements(parstr) le 0) then goto, fitdisp_bypassed
        ;fpar = [1.0,1.0,1.0,0.0,0.0,0.0]
        ;tokens = strsplit(parstr[0],' ,',/extract) ; tokenize the string
        ;for i=0,n_elements(tokens)-1 do begin ; convert each token into a float
        ;    reads, tokens[i], af
        ;    fpar[i] = af
        ;endfor 
        ;if ( (fpar[0] eq 0.0) and (fpar[1] eq 0.0) and (fpar[2] eq 0.0) ) then $ ; no fit parameters yet
        ;  goto, fitdisp_bypassed
        ;fpar[4] = dspace
        ;fpar[5] = ((*(*sPtr).mfoPtr)[3] eq 0)? 1.0 : (*sPtr).mev2wnos
        if ((*sPtr).fitPresent) then begin
            fpar = (*(*sPtr).fitParsPtr)
            case (*sPtr).fitFunc of
                0: yfit=(fans_Arcsin(omega,fpar))[*,0] ;; funct returns eval + partial derivatives wrt fpar
                1: yfit=(fans_Linear(omega,fpar))[*,0] ;; funct returns eval + partial derivatives wrt fpar 
                2: yfit=(fans_Quad(omega,fpar))[*,0] ;; funct returns eval + partial derivatives wrt fpar 
                3: yfit=(fans_Cubic(omega,fpar))[*,0] ;; funct returns eval + partial derivatives wrt fpar 
            endcase
            oplot, omega,yfit,linestyle=2,thick=4,color=(*sPtr).colors.magenta ; overplot fit 
        endif
        fitdisp_bypassed:
        wset, (*sPtr).winVis    ; set winVis as destination window
        device, copy=[0,0,(*sPtr).winWidth,(*sPtr).winHeight,0,0,(*sPtr).winPix] ; copy contents of winPix to winVis
        break
    end

    'outp': begin
        index = (*sPtr).outpIndex
        fdStructPtr = (*(*sPtr).oDataPtr)[index] ; get a ptr to the approp. fans data struct
        omega = (*(*fdStructPtr).energyPtr) ; energy tranfers
        totCnts = (*(*fdStructPtr).totCntsPtr) ; total counts
        totErr = (*(*fdStructPtr).totErrsPtr) ; error in counts
        ;monitor = (*(*fdStructPtr).monPtr)
        date = (*fdStructPtr).date
        title = (*fdStructPtr).title
        subtitle = 'Expt date: '+date
        unit_str = ((*(*sPtr).mfoPtr)[3] eq 0)? '(meV)' : '(!ncm!u-1!n)'
        xtitle = 'Energy Transfer '+unit_str

        ;Data is already normalized to monitor of first dataset 
        ;If using a custom monitor to normalize then need to rescale
        monConstant = (*fdStructPtr).monitor * (*fdStructPtr).prefactor
        wID = widget_info((*sPtr).tlbID,find_by_uname='USEMON')
        if (widget_info(wID,/valid)) then begin
           widget_control, wID, get_uvalue=isSet
           if (isSet) then begin
              wID = widget_info((*sPtr).tlbID,find_by_uname='MONVAL')
              if (widget_info(wID,/valid)) then begin
                 widget_control, wID, get_value=monValue
                 if (monValue gt 0.0) then begin
                     normFac = float(monValue)/monConstant
                     totErr = totErr * normFac
                     totCnts = totCnts * normFac
                     monConstant=monValue
                 endif
              endif
           endif
        endif
        
        ;; determine appropriate label based on scan type.
        switch (*fdStructPtr).base of
            'TIME': begin
                ftime = fix((*(*fdStructPtr).timePtr)[0] * 60)
                ytitle = 'Counts per '+strtrim(string(ftime),2)+' secs'
                break
            end
            
            'NEUT':
            else: begin
                ;monCnts = (*fdStructPtr).monitor * (*fdStructPtr).prefactor
                ytitle = 'Counts per '+strtrim(string(monConstant),2)+' mon'
                break
            end
        endswitch        
        ;; set winPix as destination window initially
        wset, (*sPtr).winPix

        if ( (n_elements(xrge) eq 0) or (n_elements(yrge) eq 0) ) then begin
            plot,omega,totCnts, $
              xstyle=1,ystyle=1,psym=-4,symsize=1.0,linestyle=0,charsize=1.4, $
              xtitle=xtitle,subtitle=subtitle,ytitle=ytitle,title=title
            ;; plot errors
            ;sigma = (*(*fdStructPtr).totErrsPtr)
            errplot, omega, totCnts-totErr, totCnts+totErr ; plot errors            
        endif else begin
            plot,omega,totCnts, $
              xrange=xrge,yrange=yrge, $
              xstyle=1,ystyle=1,psym=-4,symsize=1.0,linestyle=0,charsize=1.4, $
              xtitle=xtitle,subtitle=subtitle,ytitle=ytitle,title=title
            ;; plot errors
            ;sigma = (*(*fdStructPtr).totErrsPtr)
            errplot, omega, totCnts-totErr, totCnts+totErr ; plot errors
        endelse 
        wset, (*sPtr).winVis    ; set winVis as destination window
        device, copy=[0,0,(*sPtr).winWidth,(*sPtr).winHeight,0,0,(*sPtr).winPix] ; copy contents of winPix to winVis
        break
    end
    
endswitch

end
