Viewing contents of file '../idllib/contrib/buie/cw_pfile.pro'
;+
; NAME:
;    cw_pfile
; PURPOSE: (one line)
;    Widget application for plots and profiles of an image.
; DESCRIPTION:
;
; CATEGORY:
;    Compound Widgets
; CALLING SEQUENCE:
;    result = cw_pfile( parent )
;
; INPUTS:
;    parent : Widget id of parent.
;
; OPTIONAL INPUT PARAMETERS:
;
; KEYWORD PARAMETERS:
;    HCTITLE    = Title of Hard Copy.
;    PLATESCALE : Plate Scale in arcseconds per pixel (used at display time).
;                 If supplied and non-zero, display the bottom axis in
;                 arcseconds and the top axis in pixels, else display the
;                 bottom axis in pixels.
;    UVALUE     = Optional user value.
;
; OUTPUTS:
;
; COMMON BLOCKS:
;
; SIDE EFFECTS:
;
; RESTRICTIONS:
;
; PROCEDURE:
;    After placing this compound widget on a base, that base must then be
; realized. After realization, the image array and set-point coordinates are
; supplied via the WIDGET_CONTROL procedure and the SET_VALUE keyword.
;    The 'value' of cw_pfile is a structure having three tags: image, xset,
; and yset.
;
;    The following illustrates the procedure:
;
;    array      = some portion of a larger 2-D array.
;    xset, yset = set-point of the lower portion of the extracted array.
;
;    result = cw_pfile( parent )
;    Realize the hierarchy of parent.
;    WIDGET_CONTROL, result, SET_VALUE={image:array, xset:xset, yset:yset}
;
; MODIFICATION HISTORY:
;    Written by Doug Loucks, Lowell Observatory, April, 1994.
;    June, 1994, DWL, Converted to Compound Widget.
;    March 1996, MWB, added print support for DOS
;-
; -----------------------------------------------------------------------------
; Procedure cw_pfile_do
;   This procedure performs all processing for the draw widget and the
; hardcopy (Post Script) device.
; -----------------------------------------------------------------------------
PRO cw_pfile_do, state, HARDCOPY=hardcopy

;Save plotting structure system variable.
pmult = !p.multi
!p.multi = 0

IF state.shadesurf THEN bpp=8 ELSE bpp=4

IF KEYWORD_SET(hardcopy) THEN BEGIN
   ;Direct graphics to the Post Script device.
   dname = !d.name
   SET_PLOT,'ps'
   DEVICE, FILENAME='plot.ps', /PORTRAIT, XSIZE=15.0, YSIZE=15.0, $
   XOFFSET=2.5, YOFFSET=7.5, /HELVETICA, BITS_PER_PIXEL=bpp
   color1 = 0
   color2 = 0
ENDIF ELSE BEGIN
   ;Direct graphics to the draw widget.
   windo = !d.window
   WIDGET_CONTROL, state.dwinid, GET_VALUE=dwin
   WSET, dwin
   color1 = FIX( !d.n_colors * 0.9 )
   color2 = FIX( !d.n_colors * 0.6 )
ENDELSE

position = STRING( 'Centroid: xpos= ', state.xpos, '   ypos= ', $
           state.ypos, FORMAT='(A,F8.3,A,F8.3)' )
WIDGET_CONTROL, state.posid, SET_VALUE = position

WIDGET_CONTROL, state.dwinid, GET_UVALUE=image, /NO_COPY

;Scatter Profile section.
;  The plot arrays and variables are computed once, for each incoming image
;array, and stored in the user-value of the exclusive button base.
;  By incorporating these items into a structure, it is easy to determine
;if the computational steps may be skipped: If the user-value is a structure,
;no action is required.
;  Setting this user-value to a scalar zero will force the computational
;steps to be performed and the new results stored to the user-value for
;later retrieval.
;
IF (state.profile AND state.scatter) THEN BEGIN
   WIDGET_CONTROL, state.scatsavid, GET_UVALUE=scat
   scatstat = SIZE( scat )
   IF scatstat[2] NE 8 THEN BEGIN
      ;Need to perform computational steps.
      radp, image, state.xcen, state.ycen, r, i, fwhmp, $
      coeffs, /CONLY
      ir = FINDGEN( 151 ) / 150.0 * MAX( r )
      rcgauss_funct, ir, coeffs, ifit
      rmin = MIN( r, MAX=rmax )
      irmin = MIN( ir, MAX=irmax )
      xmin = 0
      xmax = MAX( [ rmax, irmax ] )
      scat = { r:r, i:i, ir:ir, ifit:ifit, fwhmp:fwhmp, xmin:xmin, xmax:xmax }
      ;Save the results.
      WIDGET_CONTROL, state.scatsavid, SET_UVALUE=scat
   ENDIF

   CASE state.pscale OF
      0.0 : BEGIN
         PLOT, scat.r, scat.i, PSYM=4, $
         XRANGE=[scat.xmin, scat.xmax], $
         XTITLE='radius (pixels)', YTITLE='counts'
         OPLOT, scat.ir, scat.ifit, COLOR=color1, LINESTYLE=2
      END
      ELSE  : BEGIN
         ra = scat.r * state.pscale
         PLOT, ra, scat.i, PSYM=4, XSTYLE=11, YSTYLE=3, YMARGIN=[4,3],$
         XTITLE='radius (arcseconds)', YTITLE='counts', $
         XRANGE=[scat.xmin*state.pscale, scat.xmax*state.pscale]
         AXIS, XAXIS=1, XSTYLE=3, XRANGE=[scat.xmin, scat.xmax], $
         XTITLE='radius (pixels)'
         OPLOT, scat.ir*state.pscale, scat.ifit, COLOR=color1, LINESTYLE=2
      END
   ENDCASE

   xpos = 0.85
   ypos = 0.85
   dy   = 0.03

   XYOUTS, xpos, ypos, 'FWHM(pixels): ' + STRING( scat.fwhmp, $
   FORMAT='(F7.2)'  ), /NORMAL, ALIGN=1.0

   IF state.pscale NE 0.0 THEN BEGIN
      ypos = ypos - dy
      XYOUTS, xpos, ypos, 'FWHM(arcsec): ' + $
      STRING( scat.fwhmp*state.pscale, FORMAT='(F7.2)'), $
      /NORMAL, ALIGN=1.0
   ENDIF

   IF KEYWORD_SET(hardcopy) THEN BEGIN
      IF state.pscale NE 0.0 THEN BEGIN
         ypos = ypos - dy
         XYOUTS, xpos, ypos, 'Plate Scale:  ' + $
         STRING( state.pscale, FORMAT='(F7.3)' ), /NORMAL, ALIGN=1.0
      ENDIF
      ypos = ypos - dy
      XYOUTS, xpos, ypos, 'Centroid-x:  ' + STRING( state.xpos, $
      FORMAT='(F7.3)' ), /NORMAL, ALIGN=1.0
      ypos = ypos - dy
      XYOUTS, xpos, ypos, 'Centroid-y:  ' + STRING( state.ypos, $
      FORMAT='(F7.3)' ), /NORMAL, ALIGN=1.0
   ENDIF
ENDIF

;Smooth Profile section.
;  The plot arrays and variables are computed once, for each incoming image
;array, and stored in the user-value of the non-exclusive button base.
;  By incorporating these items into a structure, it is easy to determine
;if the computational steps may be skipped: If the user-value is a structure,
;no action is required.
;  Setting this user-value to a scalar zero will force the computational
;steps to be performed and the new results stored to the user-value for
;later retrieval.
;
IF (state.profile AND state.smooth) THEN BEGIN
   WIDGET_CONTROL, state.smoosavid, GET_UVALUE=smoo
   smoostat = SIZE( smoo )
   IF smoostat[2] NE 8 THEN BEGIN
      ;Need to perform computational steps.
      dr = 0.5
      imsize = state.imsize
      xcen = state.xcen
      ycen = state.ycen
      rmax = MIN( [ xcen, ycen, imsize-xcen, imsize-ycen ] )
      ringprof, image, state.xcen, state.ycen, $
      rmax, dr, 0.0, rout, iout
      xmin = 0
      xmax = MAX( rout )
      smoo = { rout:rout, iout:iout, xmin:xmin, xmax:xmax }
      ;Save the results.
      WIDGET_CONTROL, state.smoosavid, SET_UVALUE=smoo
   ENDIF

   CASE state.pscale OF
      0.0 : BEGIN
         IF state.scatter THEN BEGIN
            OPLOT, smoo.rout, smoo.iout, COLOR=color2
         ENDIF ELSE BEGIN
            PLOT, smoo.rout, smoo.iout, $
            XRANGE=[smoo.xmin, smoo.xmax], $
            XTITLE='radius (pixels)', YTITLE='counts'
            OPLOT, smoo.rout, smoo.iout, PSYM=5
         ENDELSE
      END
      ELSE  : BEGIN
         ra = smoo.rout * state.pscale
         IF state.scatter THEN BEGIN
            OPLOT, ra, smoo.iout, COLOR=color2
         ENDIF ELSE BEGIN
            PLOT, ra, smoo.iout, XSTYLE=11, YSTYLE=3, YMARGIN=[4,3], $
            XTITLE='radius (arcseconds)', YTITLE='counts', $
            XRANGE=[smoo.xmin*state.pscale, smoo.xmax*state.pscale]
            AXIS, XAXIS=1, XSTYLE=3, XRANGE=[smoo.xmin, smoo.xmax], $
            XTITLE='radius (pixels)'
            OPLOT, ra, smoo.iout, PSYM=5
         ENDELSE
      END
   ENDCASE

ENDIF

;Histogram section.
IF state.histogram THEN BEGIN
   stats, image, /ROBO
ENDIF

;Contour plot section.
IF state.contour THEN BEGIN
   x = state.xset + INDGEN( state.imsize )
   y = state.yset + INDGEN( state.imsize )
   IF !order EQ 1 THEN $
      yr=maxmin(y) $
   ELSE $
      yr=minmax(y)
   contour, image, x, y, YRANGE=yr, XTITLE='X-pos (pixels)', $
      YTITLE='Y-pos (pixels)'
ENDIF

;Shade Surface section.
IF state.shadesurf THEN BEGIN
   x = state.xset + INDGEN( state.imsize )
   y = state.yset + INDGEN( state.imsize )
   IF !order EQ 1 THEN $
      yr=maxmin(y) $
   ELSE $
      yr=minmax(y)
   shade_surf, image, x, y, YRANGE=yr, XTITLE='X-pos (pixels)', $
      YTITLE='Y-pos (pixels)', ZTITLE='Counts'
ENDIF

IF KEYWORD_SET(hardcopy) THEN BEGIN
   ;Add some text to the hard copy and close the Post Script device.
   XYOUTS, 0.6, 1.02, state.hctitle, /NORMAL, ALIGN=0.5
   XYOUTS, 0.6, -0.07, SYSTIME(), /NORMAL
   DEVICE, /CLOSE
   SET_PLOT, dname
   IF !version.os_family EQ 'unix' THEN BEGIN
      SPAWN, 'lpr -r plot.ps'
   ENDIF ELSE IF !version.os_family EQ 'Windows' THEN BEGIN
      SPAWN, 'copy plot.ps lpt1:'
      SPAWN, 'del plot.ps'
   ENDIF
ENDIF ELSE BEGIN
   ;Reset the draw window.
   WSET, windo
ENDELSE

!p.multi = pmult

WIDGET_CONTROL, state.dwinid, SET_UVALUE=image, /NO_COPY
END


; -----------------------------------------------------------------------------
; Procedure cw_pfile_svl
; The 'set value' procedure for this compound widget.
; -----------------------------------------------------------------------------
PRO cw_pfile_svl, id, value

;Retrieve the state.
stash = WIDGET_INFO( id, /CHILD )
WIDGET_CONTROL, stash, GET_UVALUE=state, /NO_COPY

IF state.lock THEN BEGIN
   ;The value (display) is frozen. Ignore the request.
   WIDGET_CONTROL, stash, SET_UVALUE=state, /NO_COPY
   RETURN
ENDIF

; Get input image information.
stat = SIZE( value.image )
xsize = stat[ 1 ]
ysize = stat[ 2 ]

IF xsize EQ ysize THEN BEGIN
   imsize = xsize

   ; Find image maximum and then compute the centroid.
   s = WHERE( value.image EQ MAX( value.image ), count )
   s = s[ 0 ]
   xmax = s MOD imsize
   ymax = s / imsize
   centrod, value.image, xmax, ymax, 5.0, 0.0, 0.0, 0.0, xcen, ycen, counts

   state.imsize = imsize
   state.xcen = xcen
   state.ycen = ycen
   state.xpos = value.xset + xcen
   state.ypos = value.yset + ycen
   state.xset = value.xset
   state.yset = value.yset

   ;Put the profile image array into its holding area (User value of
   ;the draw widget).
   WIDGET_CONTROL, state.dwinid, SET_UVALUE=value.image

   ;Set the storage area for the computed smooth profile parameters to zero.
   ;This forces computation when smooth profile is selected.
   WIDGET_CONTROL, state.scatsavid, SET_UVALUE=0
   WIDGET_CONTROL, state.smoosavid, SET_UVALUE=0

   ;Do the processing.
   cw_pfile_do, state
ENDIF ELSE BEGIN
   MESSAGE, 'Profile array must be square.' + STRING( 7B ), /INFO
ENDELSE

;Restore the state.
WIDGET_CONTROL, stash, SET_UVALUE=state, /NO_COPY

END


; -----------------------------------------------------------------------------
; Procedure cw_pfile_eve
; Main event handler.
; -----------------------------------------------------------------------------
PRO cw_pfile_eve, event
WIDGET_CONTROL, event.id, /HOURGLASS

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

CASE event.id OF
   state.exitid : BEGIN
      WIDGET_CONTROL, stash, SET_UVALUE=state, /NO_COPY
      WIDGET_CONTROL, event.top, /DESTROY
      RETURN
   END

   state.hdcpyid : BEGIN
      ;Do the processing with the hardcopy flag set.
      cw_pfile_do, state, /HARDCOPY
   END

   state.lockid : BEGIN
      ;Toggle the lock state.
      state.lock = event.value
   END

   state.scatsavid : BEGIN
      ;Exclusive button events.
      ;  These events cause state flags to be set, or cleared. The procedure
      ;cw_pfile_do uses them to control its processing duties.

      CASE event.value OF
         0 : BEGIN
            ;Profile.
            state.profile   = 1
            state.histogram = 0
            state.contour   = 0
            state.shadesurf = 0
            WIDGET_CONTROL, state.scatid, SENSITIVE=1
            WIDGET_CONTROL, state.smooid, SENSITIVE=1
         END

         1 : BEGIN
            ;Histogram.
            state.profile   = 0
            state.histogram = 1
            state.contour   = 0
            state.shadesurf = 0
            WIDGET_CONTROL, state.scatid, SENSITIVE=0
            WIDGET_CONTROL, state.smooid, SENSITIVE=0
         END

         2 : BEGIN
            ;Contour.
            state.profile   = 0
            state.histogram = 0
            state.contour   = 1
            state.shadesurf = 0
            WIDGET_CONTROL, state.scatid, SENSITIVE=0
            WIDGET_CONTROL, state.smooid, SENSITIVE=0
         END

         3 : BEGIN
            ;Shade Surface.
            state.profile   = 0
            state.histogram = 0
            state.contour   = 0
            state.shadesurf = 1
            WIDGET_CONTROL, state.scatid, SENSITIVE=0
            WIDGET_CONTROL, state.smooid, SENSITIVE=0
         END

         ELSE : BEGIN
            MESSAGE, 'Unknown event.', /INFO
         END
      ENDCASE
      cw_pfile_do, state
   END

   state.smoosavid : BEGIN
      ;Non-exclusive button events.
      ;  State flags are set, or cleared, for each of the profile types. If an
      ;event would cause all flags to be set to the zero state, no action is
      ;taken (the remaining selected state is preserved).

      ok = 1

      CASE event.value OF
         0 : BEGIN
            ;Scatter Profile.
            newscatter = (state.scatter + 1) MOD 2
            IF newscatter EQ 0 AND state.smooth EQ 0 THEN BEGIN
               ;This would set all flags to zero. Do not accept the request.
               WIDGET_CONTROL, state.scatid, SET_BUTTON=1
               ok = 0
            ENDIF ELSE BEGIN
               state.scatter = newscatter
            ENDELSE
         END

         1 : BEGIN
            ;Smooth Profile.
            newsmooth = (state.smooth + 1) MOD 2
            IF newsmooth EQ 0 AND state.scatter EQ 0 THEN BEGIN
               ;This would set all flags to zero. Do not accept the request.
               WIDGET_CONTROL, state.smooid, SET_BUTTON=1
               ok = 0
            ENDIF ELSE BEGIN
               state.smooth = newsmooth
            ENDELSE
         END

         ELSE : BEGIN
            MESSAGE, 'Unknown event.', /INFO
         END
      ENDCASE

      IF ok THEN cw_pfile_do, state
   END

   ELSE : BEGIN
      MESSAGE, 'Outside event received. Uvalue= ' + uvalue, /INFO
      HELP, event, /STRUCTURE
   END
ENDCASE

;Restore the state.
WIDGET_CONTROL, stash, SET_UVALUE=state, /NO_COPY
END


; -----------------------------------------------------------------------------
; Procedure cw_pfile
; -----------------------------------------------------------------------------
FUNCTION cw_pfile, parent, $
         PLATESCALE=pscale, HCTITLE=hctitle, UVALUE=uvalue

; Check pscale keyword.
IF NOT KEYWORD_SET( pscale ) THEN pscale=0.0

;Check title keyword.
IF NOT KEYWORD_SET( hctitle ) THEN hctitle=''

;Define the state structure.
state = { $
   contour:0B, $
   dwinid:0L, $
   exitid:0L, $
   hctitle:hctitle, $
   hdcpyid:0L, $
   histogram:0B, $
   imsize:0, $
   lock:0B, lockid:0L, $
   posid:0L, $
   profile:1B, $
   pscale:pscale, $
   scatsavid:0L, smoosavid:0L, $
   scatter:1B, scatid:0L, $
   shadesurf:0B, $
   smooth:0B, smooid:0L, $
   xcen:0.0, ycen:0.0, $
   xpos:0.0, ypos:0.0, $
   xset:0, yset:0 }

;Define the main base.
IF KEYWORD_SET( uvalue ) THEN BEGIN
   mainbase = WIDGET_BASE( parent, COLUMN=1, EVENT_PRO='cw_pfile_eve', $
              PRO_SET_VALUE='cw_pfile_svl', UVALUE=uvalue )
ENDIF ELSE BEGIN
   mainbase = WIDGET_BASE( parent, COLUMN=1, EVENT_PRO='cw_pfile_eve', $
              PRO_SET_VALUE='cw_pfile_svl' )
ENDELSE


;Define the main buttons.
wb = WIDGET_BASE( mainbase, ROW=1 )
state.exitid = WIDGET_BUTTON( wb, VALUE='Exit' )

;Hard Copy button.
state.hdcpyid = WIDGET_BUTTON( wb, VALUE='Hard Copy' )

;Unlocked, Locked toggle buttons.
state.lockid = cw_bgroup( wb, ['Unlock Display', 'Lock Display'], $
   /EXCLUSIVE, /FRAME, /NO_RELEASE, ROW=1, SET_VALUE=0 )

;Base for exclusive and non-exclusive buttons.
base = WIDGET_BASE( mainbase, /ROW )

;Define the exclusive buttons.
state.scatsavid = CW_BGROUP( base, ['Profile', 'Histogram', 'Contour', $
     'Shade Surface'], COLUMN=1, /EXCLUSIVE, $
     /FRAME, /NO_RELEASE, SET_VALUE=0 )

;Define the non-exclusive buttons.
state.smoosavid = CW_BGROUP( base, ['Scatter', 'Smooth'], $
     COLUMN=2, /FRAME, IDS=ids, /NONEXCLUSIVE, $
     SET_VALUE=[state.scatter, state.smooth] )

;Save the Scatter and Smooth button widget id's for later use.
state.scatid = ids[0]
state.smooid = ids[1]

;Define the position label widget.
position = STRING( 'Centroid: xpos= ', state.xpos, '   ypos= ', $
           state.ypos, FORMAT='(A,F8.3,A,F8.3)' )
state.posid = WIDGET_LABEL( mainbase, /FRAME, VALUE=position )

;Define the draw window.
state.dwinid  = WIDGET_DRAW( mainbase, XSIZE=400, YSIZE=400 )

;Set the state.
stash = WIDGET_INFO( mainbase, /CHILD )
WIDGET_CONTROL, stash, SET_UVALUE=state, /NO_COPY

RETURN, mainbase

END