Viewing contents of file '../idllib/contrib/buie/cw_osipl.pro'
;+
; NAME:
;  cw_osipl
; PURPOSE:
;  Display the various spectra generated by xdavg 
; DESCRIPTION:
;
;  This program takes spectra, badflags, and profiles and displays them via a
;    multiple page format.  Several spectra can be plotted at once by selecting
;    the number of columns and rows to use to display the spectra.  If profiles
;    are provided then they are plotted alongside the spectra.  A smoothing
;    factor can also be applied to the spectra to reduce the effects of noise.
;    The y-range of the plots can be specified by switching to the "fixed"
;    option.  The automatic y-range is a min-max range.  The current page can
;    also be printed out or all pages and the printer to which they go be
;    specified
;
; CATEGORY:
;  Compound widget
; CALLING SEQUENCE:
;  cw_osipl,leader,type
; INPUTS:
;  leader - the Id of the mainbase of the calling widget
;  type   - a flag indicating whether or not the the plot is called from the top
;           or the bottom of the xdavg tool.  Different defaults are implied by
;           this for the number of rows and columns as well as the size cw_osipl
; OPTIONAL INPUT PARAMETERS:
; KEYWORD INPUT PARAMETERS:
;  TITLE - Optional title of widget, default = 'OSIRIS XD PLOT'
; OUTPUTS:
;  Spectra and profiles are plotted to the screen and can be printed
; KEYWORD OUTPUT PARAMETERS:
; COMMON BLOCKS:
; SIDE EFFECTS:
; RESTRICTIONS:
; PROCEDURE:
;  
;  This plot widget is used by creating it as a compound widget from within the
;    calling program and recording its Id.  Plots are then displayed by the
;    program by setting its value to the following data structure in a
;    widget_control statement:
;
;  data={
;
;        *Required information*
;
;        nspecs:  The number of spectra that need to be plotted
;        calib:   The calibration structure for the spectra
;        pref:    The index of the spectrum that is prefered to be displayed
;        filenam: The filename of the spectrum, this added to the objname field
;                 is displayed as the title of the plot
;        objname: The name of the object
;        display: The array of spectra to be plotted
;        badflag: The array of badflags to be plotted
;        pflag:   Flag indicating whether profiles are to be hidden
;        cflag:   Flag indicating whether a each order of the spetrum should be
;                 plotted in a different color
;        pageflg: Flag to indicate that the plotter should only replot if the
;                 prefered spectrum is not on the current page
;
;        *Optional information*
;
;          (required if pflag is set)
;        prof:    The array of profiles values
;        index:   The indices for the profile points
;        upbnd:   The upper bound values for the sky level for the profiles
;        lwbnd:   The lower bound values for the sky level for the profiles
;        relsig:  The signal relative to the brightest spectrum in the set
;        mate:    The mate used in the extraction by xdspec
;
;        airmass: The airmass for each spectrum
;        juldate: The Julian date of the exposure
;        exptime: The exposure time of the spectrum
;        ncoads:  The # of coadds used to make the spectrum
;
;        } - UVALUE of state.mainbase
;
; MODIFICATION HISTORY:
;  98/06/12 - Written by Chris Dalla Piazza, Lycoming College
;-

;-------------------------------------------------------------------------------
; Procedure cw_osipl_display
;
; This procedure takes the selected page number, data, and plots them.  It
; figures out what the bounds of the various windows are for multiple plots and
; sets defaults for things like the y-range, color table, and smoothing factor.
; The profiles are also plotted if they are requested.
;-------------------------------------------------------------------------------
pro cw_osipl_display,state,data,calib

   ; Make sure that the page number selected by the various user inputs does not
   ; exceed the total number of pages required to display the spectra

   if state.page gt state.tot then state.page=state.tot

   ; Find the begining and end indices for the spectra on that page

   idx=state.col*state.row*(state.page-1)
   if idx+1 gt data.nspecs then idx=idx-state.col*state.row
   en=idx+state.col*state.row
   if en gt data.nspecs-1 then en=data.nspecs
   en=en-1

   ; Figure out the various size parameters for the plots and the coordinates
   ; of the lower left and upper right corners for each plot.

   i=idx-1
   erase

   ; A little bit of space is required on all sides of the plots so labels do
   ; not clash.  These are the insets

   li=67.0/state.xsize ;left
   ri=5.0/state.xsize  ;right
   ti=35.0/state.ysize ;top
   bi=42.0/state.ysize ;bottom

   ; Insets for the profile plot windows

   pli=30.0/state.xsize
   pri=5.0/state.xsize
   pbi=24.0/state.ysize
   pti=5.0/state.ysize

   ; Set the values for the smoothing and color keywords

   if state.smooth eq 1 then smooth=0 else smooth=state.smooth
   if !d.name eq 'PS' then color=0 else if data.cflag then begin
      tvlct,[0,255,0,0,255],[0,0,255,0,255],[0,0,0,255,255]
      color=[1,2,3,1]
   endif else color=!d.n_colors-1

   ; Cycle through the columns first and the rows second to produce the plots
   ; Changing the order of for loops here changes column, row order of plotting
   ; Changing the increment between +1 to -1 changes top to bottom, bottom to
   ; top order of plotting as well as right to left, left to right.

   for k=state.row-1,0,-1 do begin

      for j=0,state.col-1 do begin

         if data.pflag eq 1 then begin

            ; These are the coordinates for the plot without profiles included

            x0=float(j)/state.col+li
            y0=float(k)/state.row+bi
            x1=float(j+1)/state.col-ri
            y1=float(k+1)/state.row-ti
         endif else begin

            ; These are the coordinates for the plot with profiles

            specxsize=0.75/state.col
            profxsize=0.25/state.col
            x0=float(j)/state.col+li
            y0=float(k)/state.row+bi
            x1=(j+1)*specxsize+j*profxsize-ri
            y1=float(k+1)/state.row-ti

         endelse

         i=i+1

         if i le en then begin

            ; set the y range for the plot, excluding badpoints and NaN's

            yr=fltarr(2)
            case state.scale OF
               0: begin
                  z=where(data.badflag[*,i] eq 0 and $
                          finite(data.display[*,i]) eq 1,count)
                  if count ne 0 then begin
                     newdat=data.display[z,i]
                     newdat=newdat[sort(newdat)]
                     midpt=long(n_elements(newdat)/2)
                     lowpt=long(n_elements(newdat)*0.1)
                     hipt=long(n_elements(newdat)*0.9)
                     yr[0]= newdat[midpt] - (newdat[midpt]-newdat[lowpt])*5.5/4.0
                     yr[1]= newdat[midpt] + (newdat[hipt]-newdat[midpt])*5.5/4.0
                  endif else begin
                     yr=[0.,1.]
                  endelse
               end
               1: begin
                  z=where(data.badflag[*,i] eq 0 and $
                           finite(data.display[*,i]) eq 1,count)
                  if count ne 0 then begin
                     yr[0]=min(data.display[z,i])
                     yr[1]=max(data.display[z,i])
                  endif else begin
                     yr=[0.,1.]
                  endelse
               end
               2: begin
                  widget_control,state.scalelow,get_value=low
                  yr[0]=float(low)
                  widget_control,state.scalehi,get_value=hi
                  yr[1]=float(hi)
               end
            endcase
            if state.scale eq 1 then begin
            endif else begin
            endelse

            ; Plot the spectrum
            case state.smotype OF
               0: begin
                  lowess=2
               end
               1: begin
                  lowess=0
               end
            endcase

            plotspec,data.calib,data.display[*,i],BADFLAGS=data.badflag[*,i], $
                  title=data.filenam[i]+' '+data.objname[i],yr=yr,LOWESS=lowess, $
                  SMOOTH=smooth,position=[x0,y0,x1,y1],/noerase,COLOR=color, $
                  PSYM=state.psym*state.psymsign

            ; Plot the profiles if they have been requested

            if data.pflag eq 0 then begin

               ; Find the coordinates of its plot window, it uses the same y 
               ; dimensions as the spectrum plot

               x0=(j+1)*specxsize+j*profxsize+pli
               x1=float(j+1)/state.col-pri

               tstring=string(data.mate[i],data.relsig[i], $
                              format='("mate:",i3.3,1x,"RelSig=",f4.2)')

               ; Plot the profiles and the bounds of the sky background

               plot,data.index[*,i],data.prof[*,i],position=[x0,y0,x1,y1], $
                  /noerase,yr=[-1.0,1.0],xtitle='Row number', $
                  PSYM=state.psym*state.psymsign,title=tstring

               oplot,data.index[*,i],data.lwbnd[*,i],linestyle=2

               oplot,data.index[*,i],data.upbnd[*,i],linestyle=2

            endif

         endif

      endfor  ; column loop

   endfor  ; row loop

   ; Extra plot page annotation if this is a hardcopy.
   if state.hard ne 0 then begin
      jdstr,data.juldate[0],100,str
      xyouts,0.98,0.02,str,align=1.0,/normal
   endif

end

;-------------------------------------------------------------------------------
; Procedure cw_osipl_plot
;
; This procedure figures out what the format of the plot will be and handles
; printing options.  It figures out how many pages are in the plot and which
; page to be on.  It also sets up the required commands for printing out the
; plots.  Sensitization of the paging buttons is also handled here
;-------------------------------------------------------------------------------
pro cw_osipl_plot,state

   widget_control,state.mainbase,update=0

   widget_control,state.mainbase,get_uvalue=data,/no_copy

   ; Figure out which page number the prefered spectrum is on

   if data.pref eq 0 then begin
      data.pref=where(data.filenam eq state.prefnam)+1
      if data.pref eq 0 then data.pref=1
   endif

   ; IF the plotter is to only update the plot if the prefered spectrum is not on
   ; the page and the page isn't different then just drop everything and stop

   oldpage=state.page
   state.page=ceil(float(data.pref)/float(state.col*state.row))
   if data.pageflg eq 1 and oldpage eq state.page then begin
      data.pageflg=0
      data.pref=(state.page-1)*state.col*state.row
      state.prefnam=data.filenam[data.pref]
      data.pref=0
      widget_control,state.mainbase,set_uvalue=data,/no_copy
      widget_control,state.mainbase,update=1
      return
   endif

   ; Set the device to printer if a hardcopy is needed

   if state.hard gt 0 then begin
      mydevice=!D.NAME
      set_plot,'ps'
      cd,'.',current=dir
      dir=dir+'/'
      if state.row gt state.col then begin
         setpage,/portrait
      endif else begin
         setpage,/landscape
      endelse
   endif else begin
      ;Direct graphics to the draw widget.
      windo = !d.window
      WIDGET_CONTROL, state.plotid, GET_VALUE=dwin
      wset, dwin
   endelse

   ; Figure out the page number and page format

   state.tot=ceil(float(data.nspecs)/float(state.col*state.row))

   if state.hard eq 2 then begin

      store=state.page

      for page=1,state.tot do begin

         state.page=page
         cw_osipl_display,state,data,data.calib

      endfor

      state.page=store

   endif else begin

      cw_osipl_display,state,data,data.calib

   endelse

   ; Print the hardcopy and reset the printing flag

   if state.hard gt 0 then begin
      device,/close
      if state.printer eq '' then begin
         spawn,'lp idl.ps'
      endif else begin
         spawn,'lp -d '+state.printer+' idl.ps'
      endelse
      set_plot,mydevice
      state.hard=0
   endif else begin
      ;Reset the draw window.
      wset, windo
   endelse

   ; Sensitize the paging buttons

   if state.page eq state.tot then begin
      widget_control,state.butforw,sensitive=0
   endif else begin
      widget_control,state.butforw,sensitive=1
   endelse
   if state.page eq 1 then begin
      widget_control,state.butback,sensitive=0
   endif else begin
      widget_control,state.butback,sensitive=1
   endelse

   ; Find the name of the new prefered spectrum

   data.pref=(state.page-1)*state.col*state.row
   state.prefnam=data.filenam[data.pref]
   data.pref=0

   widget_control,state.lblhere,set_value=strcompress(state.page)
   widget_control,state.lbltot,set_value=strcompress(state.tot)
   widget_control,state.mainbase,set_uvalue=data,/no_copy

   widget_control,state.mainbase,update=1

end

;-------------------------------------------------------------------------------
; Procedure cw_osipl_svl
;
; This is the procedure to be followed when the plot widget's value is set.
; This essentially consists of running the plot procedure just as if an event
; had been generated.
;-------------------------------------------------------------------------------
pro cw_osipl_svl,id,data

   stash=widget_info(id,/CHILD)
   widget_control,stash,get_uvalue=state,/no_copy   
   widget_control,state.mainbase,set_uvalue=data,/no_copy

   cw_osipl_plot,state

   widget_control,stash,set_uvalue=state,/no_copy

end

;-------------------------------------------------------------------------------
; Procedure cw_osipl_eve
;
; Event handler for the cw_osipl widget
;-------------------------------------------------------------------------------
pro cw_osipl_eve,event

   WIDGET_CONTROL, event.id, /HOURGLASS

   stash = WIDGET_INFO( event.handler, /CHILD )
   WIDGET_CONTROL, stash, GET_UVALUE=state, /NO_COPY

   case event.id of

      state.mainbase: begin

         ; Get the size of the widget holding the options and subtract it off of
         ; the size of the mainbase, then set the size of the plot window to this
         ; and refresh the plot

         baseinfo=widget_info(state.baseid,/geometry)
         state.xsize=event.x
         state.ysize=event.y-baseinfo.ysize
         widget_control,state.plotid,xsize=state.xsize,ysize=state.ysize
         cw_osipl_plot,state

      end

      state.butcol: begin
         state.col=event.index+1
         cw_osipl_plot,state
      end

      state.butrow: begin
         state.row=event.index+1
         cw_osipl_plot,state
      end

      state.butforw: begin

         ; Increase the value of the current page number by one and replot

         widget_control,state.lblhere,get_value=value
         widget_control,state.lblhere,set_value=strcompress(value+1)
         widget_control,state.mainbase,get_uvalue=data,/no_copy
         state.prefnam=data.filenam[(value)*state.row*state.col]
         widget_control,state.mainbase,set_uvalue=data,/no_copy
         cw_osipl_plot,state

      end

      state.butback: begin

         ; Decrease the value of the current page number by one and replot

         widget_control,state.lblhere,get_value=value
         widget_control,state.lblhere,set_value=strcompress(value-1)
         widget_control,state.mainbase,get_uvalue=data,/no_copy
         state.prefnam=data.filenam[(value-2)*state.row*state.col]
         widget_control,state.mainbase,set_uvalue=data,/no_copy
         cw_osipl_plot,state

      end

      state.psymid: begin
         state.psym=event.index
         cw_osipl_plot,state
      end

      state.psymsignid: begin
         if event.index eq 0 then $
            state.psymsign = -1 $
         else $
            state.psymsign = 1
         cw_osipl_plot,state
      end

      state.smoothid: begin
         state.smooth=event.value
         cw_osipl_plot,state
      end

      state.smotypeid: begin
         state.smotype=event.index
         cw_osipl_plot,state
      end

      state.butscale: begin
         state.scale=event.index

         case event.index OF
            ; Auto scaling
            0: begin
               ; Desensitize the min-max text widgets
               widget_control,state.scalelow,sensitive=0
               widget_control,state.scalehi,sensitive=0
            end
            ; Min/max scaling
            1: begin
               ; Desensitize the min-max text widgets
               widget_control,state.scalelow,sensitive=0
               widget_control,state.scalehi,sensitive=0
            end
            ; Fixed scaling
            2: begin
               ; Set the value of the min-max to be the min-max of the first
               ;   spectrum in the set as a default value
               widget_control,state.mainbase,get_uvalue=data,/no_copy

               z=where(data.badflag[*,0] eq 0 and $
                       finite(data.display[*,0]) eq 1,count)
               if count ne 0 then begin
                  newdat=data.display[z,0]
                  newdat=newdat[sort(newdat)]
                  midpt=long(n_elements(newdat)/2)
                  lowpt=long(n_elements(newdat)*0.1)
                  hipt=long(n_elements(newdat)*0.9)
                  lowval = 0.0
                  hival  = newdat[midpt] + (newdat[hipt]-newdat[midpt])*5.5/4.0
               endif else begin
                  lowval = 0.
                  hival = 0.
               endelse

               widget_control,state.scalelow,set_value= $
                  strcompress(string(lowval,format='(g8.2)'),/remove_all)
               widget_control,state.scalehi,set_value= $
                  strcompress(string(hival,format='(g8.2)'),/remove_all)

               widget_control,state.mainbase,set_uvalue=data,/no_copy
               widget_control,state.scalelow,sensitive=1
               widget_control,state.scalehi,sensitive=1
            end
            else: begin
               print,'CW_OSIPL: Warning!  Illegal scaling index.'
            end
         endcase

         cw_osipl_plot,state
      end

      state.scalelow: begin
         cw_osipl_plot,state
      end

      state.scalehi: begin
         cw_osipl_plot,state
      end

      state.butphard: begin
         state.hard=1
         cw_osipl_plot,state
      end

      state.butahard: begin
         state.hard=2
         cw_osipl_plot,state
      end

      state.printid: begin

         ; Get the new printer and make sure that it is a valid printer

         widget_control,state.printid,get_value=printer
         printer=strcompress(printer)
         spawn,'lpq -P '+printer[0],result
         if result[0] eq '' then begin
            print,'Invalid printer.  Keeping old setting.'
            widget_control,state.printid,set_value=state.printer
         endif else begin
            state.printer=printer[0]
         endelse

      end

      ELSE : BEGIN
         MESSAGE, 'Unknown event:', /INFO
         HELP, event, /STRUCTURE
      END

   endcase

   WIDGET_CONTROL, stash, SET_UVALUE=state, /NO_COPY

end

;-------------------------------------------------------------------------------
; Function cw_osipl
;
; This Function returns the Id of the mainbase and creates the layout of the
; widget
;-------------------------------------------------------------------------------
function cw_osipl,leader,type,TITLE=title

   if badpar(title,[0,7],0,caller='CW_OSIPL: (TITLE) ', $
                           default='OSIRIS XD PLOT') then begin
      help,title
      title='OSIRIS XD PLOT'
      print,'CW_OSIPL: Warning, illegal TITLE value provided, using default.'
   endif

   ; Set the default size and number of rows depending on the type of plot

   if type eq 0 then begin
      xsize=740
      ysize=600
      row=4
   endif else begin
      xsize=730
      ysize=300
      row=1
   endelse

   setusym,1

   ; Define the state structure

   state={mainbase: 0L, $ ; Id of the mainbase
;          labelid:  0L, $ ; Id of the widget bas holding the user options widgets
          plotid:   0L, $ ; Id of the plot widget
          baseid:   0L, $ ; Id of base holding all of the widgets
          xsize:    xsize, $ ; The current width of the widget
          ysize:    ysize, $ ; The current height of the widget
          prefnam:  '', $ ; The spectrum name of the last prefered spectrum
          hard:     0, $  ; The flag indicating the which kind of print is
                          ; requested: 0 - indicates no print, 1 - indicates print
                          ; just that page, 2 - indicates print all pages
          col:      1, $  ; The current number of columns to plot spectra in
          row:      row, $ ; The current number of rows to plot spectra in
          scale:    0, $  ; Flag indicating whether auto or fixed scaling is to be
                          ; used for the y-range of the plot
          page:     1, $  ; The current page number selected
          tot:      1, $  ; The total number of pages
          printer:  '', $ ; The name of the printer to use
          smooth:   1, $  ; The smoothing scale to use
          psym:     0, $  ; Plot symbol to use.
          psymsign: -1, $  ; Positive: just symbols, negative: connect the dots.
          smotype:  0, $ ; Smoothing type to use on plots.
          smoothid: 0L, $ ; Id of the smoothing slider
          psymid:   0L, $ ; Id of the plot symbol droplist
          psymsignid: 0L, $ ; Id of the plot line droplist
          smotypeid: 0L, $ ; Id of the smoothing type widget
          printid:  0L, $ ; Id of the printer text
          scalelow: 0L, $ ; Id of the min text
          scalehi:  0L, $ ; Id of the max text
          butrow:   0L, $ ; Id of the row droplist
          butcol:   0L, $ ; Id of the col droplist
          butback:  0L, $ ; Id of the prev page button
          butscale: 0L, $ ; Id of the scaling selection button group
          butphard: 0L, $ ; Id of the one page printing button
          butahard: 0L, $ ; Id of the all pages printing button
          lblhere:  0L, $ ; Id of the currently selected page label
          lbltot:   0L, $ ; Id of the total # of pages label
          butforw:  0L}   ; Id of the next page button

   ; Define the main base

   mainbase=WIDGET_BASE(TITLE=title, EVENT_PRO='cw_osipl_eve', $
            PRO_SET_VALUE='cw_osipl_svl',/COLUMN,/TLB_SIZE_EVENTS, $
            GROUP_LEADER=leader)

   state.baseid=WIDGET_BASE(mainbase,/column,/FRAME)
   b2=state.baseid

   ; Top part of the widget that contains the user options

   b=widget_base(b2,/row)
   c=widget_base(b,/column,/frame)

   state.butrow=WIDGET_DROPLIST(c,TITLE=' Rows  ',VALUE=['1','2','3','4','5','6', $
           '7','8','9','10','11','12','13','14','15','16','17','18','19','20'], $
            UVALUE=1)
   widget_control,state.butrow,set_droplist_select=row-1

;   state.labelid=WIDGET_LABEL(b,VALUE='by')

   state.butcol=WIDGET_DROPLIST(c,TITLE='Columns',VALUE=['1','2','3','4','5'], $
                UVALUE=1)

   c=widget_base(b,/column,/frame)
   cc=widget_base(c,/row)
   state.butback=WIDGET_BUTTON(c,VALUE='Prev Page')
   state.butforw=WIDGET_BUTTON(c,VALUE='Next Page')

   b1=WIDGET_LABEL(cc,VALUE='Page')
   state.lblhere=WIDGET_LABEL(cc,VALUE='1',/DYNAMIC_RESIZE)
   b1=WIDGET_LABEL(cc,VALUE='of')
   state.lbltot=WIDGET_LABEL(cc,VALUE='1',/DYNAMIC_RESIZE)

   c=widget_base(b,/column,/frame)
   state.smoothid=widget_slider(c,minimum=1,maximum=10,TITLE='Smoothing Factor')
   state.smotypeid=widget_droplist(c,TITLE='', $
      VALUE=['Lowess','Boxcar'])

   c=widget_base(b,/column,/frame)

   c=widget_base(b,/column,/frame)
   state.butphard=WIDGET_BUTTON(c,VALUE='Page Hardcopy')
   state.butahard=WIDGET_BUTTON(c,VALUE='All Hardcopy')
   cc=widget_base(c,/row)
   b1=widget_label(cc,value='Printer:')
   state.printid=widget_text(cc,value='',/editable,xsize=4)

   b=widget_base(b2,/row)

;   state.butscale=CW_BGROUP(b,['Auto Scaling','Fixed Scaling'],/no_release, $
;                          /frame,/exclusive,row=1,set_value=0)
   state.butscale=WIDGET_DROPLIST(b,TITLE='', $
      VALUE=['Auto Scaling','Min/max Scaling','Fixed Scaling'])
   b1=WIDGET_LABEL(b,value='Ymin:')
   state.scalelow=widget_text(b,value='',/editable,xsize=8)
   b1=widget_label(b,value='Ymax:')
   state.scalehi=widget_text(b,value='',/editable,xsize=8)
   state.psymid=WIDGET_DROPLIST(b, $
      VALUE=['No Sym','Plus','Asterisk','Dot','Diamond', $
             'Triangle','Square','X','Circle'])
   state.psymsignid=WIDGET_DROPLIST(b,VALUE=['Lines','No Lines'])

   ; Bottom part of the widget that is the plotting window

   state.plotid=WIDGET_DRAW(mainbase,xsize=state.xsize,ysize=state.ysize)

   state.mainbase=mainbase

   widget_control,state.scalelow,sensitive=0
   widget_control,state.scalehi,sensitive=0

   stash=WIDGET_INFO(mainbase,/CHILD)
   WIDGET_CONTROL,stash,SET_UVALUE=state,/NO_COPY
   WIDGET_CONTROL,mainbase,/REALIZE,/MAP

   return,mainbase

end