Viewing contents of file '../idllib/contrib/buie/ccdphot.pro'
;+
; NAME:
;    ccdphot
; PURPOSE: (one line)
;    General purpose display and processing of CCD image files (FITS).
; DESCRIPTION:
;    A general purpose widget application which displays and processes CCD
; image files. This includes bias and flat field calibration and photometry
; reduction (itool).
;    This procedure supports FITS files from the LCCD and LORAL instruments.
; CATEGORY:
;    Widgets
; CALLING SEQUENCE:
;    ccdphot[, keywords]
; INPUTS:
;
; OPTIONAL INPUT PARAMETERS:
;
; KEYWORD PARAMETERS:
;    CALIBFILE=    : Calibration startup file.  Default is CALIBPATH/files.cal
;    CALIBPATH=    : Path for calibration files.  Default is PATH/calib
;    KEYLIST=      : Name of a file containing a correspondence list. This list
;                    associates a set of standard names with the actual keyword
;                    names found in a FITS file header. If this keyword is
;                    omitted, a default list is used, as if a file with the
;                    following contents had been supplied:
;                     AIRMASS   K  AIRMASS
;                     DATE      K  DATE-OBS
;                     DATETMPL  T  DD-MM-YYYY
;                     EXPDELTA  V  0.0
;                     EXPTIME   K  EXPTIME
;                     FILTER    K  FILTERS
;                     FILENAME  K  CCDFNAME
;                     OBJECT    K  OBJECT
;                     UT        K  UT 
;                    The middle column is a flag. It may be K, for Keyword,
;                    T, for Template, or V, for Value. If it is V, the contents
;                    of the third field on that line should make sense for the
;                    name in the first field.
;
;    PATH=         : Optional path for image and calibration directory.
;                    If not specified, the current directory is used.
;    PHOTPARMFILE= : Optional photometry parameter file.  Passed to Itool.
;    TMPLFILE=     : Optional template file.  Passed to Itool.
;    WZOOMFACT=    : Forces the cw_itool main draw window to have a specified
;                    zoom factor (passed to cw_itool).
;
; OUTPUTS:
;
; COMMON BLOCKS:
;
; SIDE EFFECTS:
;
; RESTRICTIONS:
;
; PROCEDURE:
;
; MODIFICATION HISTORY:
;    Written by Doug Loucks, Lowell Observatory, December, 1993.
;    Created by combining lccd.pro, loral.pro, and ccdphot_inst_init.pro.
;    Added features include: Correspondence list for FITS header keywords
;       and recognition of old format calibration environment files.
;    1/26/94, DWL, Added header date-parsing code to ldimage procedure.
;    3/2/94,  DWL, Added keylist item EXPDELTA.
;    3/3/94,  DWL, Improved the template and header date-parsing process.
;    3/10/94, DWL, Bug fixes.
;    4/94,    DWL, Thorough validation of all environment parameters (overscan,
;       cropping region, and bias/flat size compatibility).
;    3/1/95,  MWB, Added support for a dark count calibrator (optional).
;    6/8/95,  MWB, Changed defaults on calibration paths.  Also, if file
;                  name begins with "+", CALIBPATH will replace the "+".
;    10/31/95, MWB, Added CATCH error handler to trap bad image file reads
;                  using READFITS.  This prevents a crash to the IDL prompt
;                  when and invalid file is read.
;    3/18/96, MWB, Minor change to code that reads UT from header.  Format
;                  requires two colons in string (HH:MM:SS) but also now
;                  allows SS to be a floating point number (ie., SS.sss).
;    6/25/96, MWB, implemented AUTO(-photometry) button.
;    1/22/97, MWB, fixed AUTO infinite loop if bad template positions found.
;    2/5/97, MWB, added FWHM and Mag safety shutoff in AUTO
;    6/13,97, MWB, extracted keylist to external general routines.
;-

; ------------------------------------------------------------------------------
; Internal support procedures.
; A list of the procedures in this file (in order of occurrence) are:
;
;   ccdphot_fmt      Format the filter list.
;   ccdphot_di       Display image with cw_itool.
;   ccdphot_pr       Performs bias and flat field correction.
;   ccdphot_lf       Loads a flat frame image into memory.
;   ccdphot_af       Adds a new filter code to the list of codes.
;   ccdphot_li       Loads an image into memory.
;   ccdphot_lb       Loads a bias frame image into memory.
;   ccdphot_ld       Loads a bias frame image into memory.
;   ccdphot_lc       Loads calibration environment files.
;   ccdphot_sc       Saves the current calibration environment.
;   ccdphot_it_eve   Event handler for cw_itool TLB.
;   ccdphot_edit_eve Event handler for calibration editor TLB.
;   ccdphot_eve      Event handler for ccdphot.
;   ccdphot
;
; ------------------------------------------------------------------------------
; Function ccdphot_fmt
; ------------------------------------------------------------------------------
FUNCTION ccdphot_fmt, flats

;The first element of the flats array represents the unity flat and is not
;available for modification.

n = N_ELEMENTS( flats )

;Find length of the longest filtercode name.
IF n GT 1 THEN BEGIN
   maxlen = MAX( STRLEN( flats[1:n-1].filtercode ) )

   ;Set alpha format to this value
   fmt = '(A' + STRTRIM( STRING( maxlen ), 2 ) + ')'

   RETURN, STRING( flats[1:n-1].filtercode, FORMAT=fmt ) + ' ' + $
           flats[1:n-1].filename
ENDIF ELSE BEGIN
   RETURN, ''
ENDELSE

END


; ------------------------------------------------------------------------------
; Procedure ccdphot_di
; Calls itool to display the image.
; ------------------------------------------------------------------------------
PRO ccdphot_di, state, AUTOPHOT=autophot, PHOTTYPE=phottype, POSITION=position

IF state.imfile EQ '' THEN RETURN

valid = WIDGET_INFO( state.cwitoolbase, /valid_id )

IF state.new AND valid  THEN BEGIN
   WIDGET_CONTROL, state.cwitoolbase, TLB_GET_OFFSET=offset
   WIDGET_CONTROL, state.cwitoolbase, /DESTROY

   state.cwitoolbase = WIDGET_BASE( TITLE='Itool', COLUMN=1, $
                   GROUP_LEADER=state.mainbase )
   WIDGET_CONTROL, state.cwitoolbase, TLB_SET_XOFFSET=offset[0], $
                   TLB_SET_YOFFSET=offset[1]
   XMANAGER, '', state.cwitoolbase, /JUST_REG

   state.cwitoolid = cw_itool( state.cwitoolbase, $
                     PHOTPARMFILE=state.photparmfile, $
                     TMPLFILE=state.tmplfile, $
                     WZOOMFACT=state.wzoomfact, $
                     XSIZE=state.pxsize, $
                     YSIZE=state.pysize )

ENDIF

IF state.cwitoolid EQ 0L THEN BEGIN
   state.cwitoolbase = WIDGET_BASE( TITLE='Itool', COLUMN=1, $
                       GROUP_LEADER=state.mainbase )
   XMANAGER, '', state.cwitoolbase, /JUST_REG
   state.cwitoolid = cw_itool( state.cwitoolbase, $
                     PHOTPARMFILE=state.photparmfile, $
                     TMPLFILE=state.tmplfile, $
                     WZOOMFACT=state.wzoomfact, $
                     XSIZE=state.pxsize, $
                     YSIZE=state.pysize )
ENDIF

;In case cw_itool has been dismissed, or the TLB has not been realized:
WIDGET_CONTROL, state.cwitoolbase, /REALIZE, /MAP

;Get the image parameters structure (the 'value' for cw_itool).
WIDGET_CONTROL, state.imparmsptr, GET_UVALUE=im_parms

;Set the pointer and size fields.
im_parms.imageptr = state.dimgptr
im_parms.xsize = state.pxsize
im_parms.ysize = state.pysize

sz=size(autophot)
IF sz[1] EQ 7 THEN BEGIN
   IF autophot EQ im_parms.object THEN BEGIN
      im_parms.autophot = 1
      im_parms.lasttype = phottype
      im_parms.lastpos  = position
   ENDIF
ENDIF

;Display the new image.
WIDGET_CONTROL, state.cwitoolid, SET_VALUE=im_parms
END


; ------------------------------------------------------------------------------
; Procedure ccdphot_pr
; Performs bias and flat field corrections.
; ------------------------------------------------------------------------------
PRO ccdphot_pr, state
bel = STRING( 7B )

IF state.imfile EQ '' THEN RETURN

msg = 'Image: ' + state.imfile

IF state.processflag EQ 0 THEN BEGIN
   IF (state.xsize NE state.pxsize) OR (state.ysize NE state.pysize) THEN BEGIN
      state.new = 1B
      state.pxsize = state.xsize
      state.pysize = state.ysize
   ENDIF ELSE BEGIN
      state.new = 0B
   ENDELSE

   state.dimgptr = state.rimgptr

   msg = msg + '  (raw).'
   PRINT, ''
   PRINT, msg
   RETURN
ENDIF

state.dimgptr = state.cimgptr

;Full size of image (including overscan region).
xsize  = state.xsize
ysize  = state.ysize

;Calibration image size.
cxsize = state.cxsize
cysize = state.cysize

;Overscan region.
xl = state.xl
xr = state.xr

;Cropping region.
x1 = state.x1
x2 = state.x2
y1 = state.y1
y2 = state.y2

IF cxsize EQ 0 THEN BEGIN
   xdim = xsize
   ydim = ysize
ENDIF ELSE BEGIN
   xdim = cxsize
   ydim = cysize
ENDELSE

IF (x1 LT 0) AND (x2 LT 0) THEN BEGIN
   IF cxsize GT 0 THEN BEGIN
      xdim = cxsize
   ENDIF ELSE BEGIN
      xdim = xsize
   ENDELSE
   x1 = 0
   x2 = xdim - 1
ENDIF ELSE BEGIN
   xdim = x2 - x1 + 1
ENDELSE

IF (y1 LT 0) AND (y2 LT 0) THEN BEGIN
   IF cysize GT 0 THEN BEGIN
      ydim = cysize
   ENDIF ELSE BEGIN
      ydim = ysize
   ENDELSE
   y1 = 0
   y2 = ydim - 1
ENDIF ELSE BEGIN
   ydim = y2 - y1 + 1
ENDELSE

IF (xdim NE state.pxsize) OR (ydim NE state.pysize) THEN BEGIN
   state.new = 1B
ENDIF ELSE BEGIN
   state.new = 0B
ENDELSE

state.pxsize = xdim
state.pysize = ydim

WIDGET_CONTROL, state.imparmsptr, GET_UVALUE=im_parms, /NO_COPY
nframes = im_parms.nframes
filter  = im_parms.filter
exptime = im_parms.exptime
WIDGET_CONTROL, state.imparmsptr, SET_UVALUE=im_parms, /NO_COPY

; Choose a flat
WIDGET_CONTROL, state.flatbase, GET_UVALUE=flats, /NO_COPY

findex = WHERE( flats[*].filtercode EQ filter, count )

IF count EQ 0 THEN BEGIN
   findex = 0
   state.fselected = 0
ENDIF

state.fselected = findex[0]

msg = msg + '   Filter: ' + flats[findex].filtercode

WIDGET_CONTROL, flats[findex].flatid, GET_UVALUE=flatframe, /NO_COPY
WIDGET_CONTROL, state.rimgptr, GET_UVALUE=rawframe, /NO_COPY
WIDGET_CONTROL, state.biasptr, GET_UVALUE=biasframe, /NO_COPY
WIDGET_CONTROL, state.darkptr, GET_UVALUE=darkframe, /NO_COPY
WIDGET_CONTROL, state.dimgptr, GET_UVALUE=image, /NO_COPY

image = FLTARR( xdim,  ydim,  nframes, /NOZERO )

; Process the image.
mean = 0
overscan = (xl GE 0) AND (xr GE 0)
FOR j=0, nframes-1 DO BEGIN
   IF overscan THEN BEGIN
      sigma = STDEV( rawframe[ xl : xr, * ], mean ) 
;     msg = msg + '   Overscan (mean, sigma): ' + $
;     STRING( mean, FORMAT='(G0.0)' ) + ', ' + $
;     STRING( sigma, FORMAT='(G0.0)' )
   ENDIF
      image[*, *, j] = (rawframe[ x1:x2, y1:y2, j ] - mean - $
      biasframe - darkframe*exptime) / flatframe
ENDFOR

WIDGET_CONTROL, flats[findex].flatid, SET_UVALUE=flatframe, /NO_COPY
WIDGET_CONTROL, state.rimgptr, SET_UVALUE=rawframe, /NO_COPY
WIDGET_CONTROL, state.biasptr, SET_UVALUE=biasframe, /NO_COPY
WIDGET_CONTROL, state.darkptr, SET_UVALUE=darkframe, /NO_COPY
WIDGET_CONTROL, state.dimgptr, SET_UVALUE=image, /NO_COPY

WIDGET_CONTROL, state.flatbase, SET_UVALUE=flats, /NO_COPY

PRINT, msg

END


; ------------------------------------------------------------------------------
; Procedure ccdphot_lf
; Loads a flat into memory. Flats are stored in the user-value of the
; base widget for each flat.
; ------------------------------------------------------------------------------
PRO ccdphot_lf, state, findex
bel = STRING( 7B )

WIDGET_CONTROL, state.flatbase, GET_UVALUE=flats, /NO_COPY

filter = 'Filter ' + flats[findex].filtercode + ' '
fname  = flats[findex].filename

fullname=fname
if strmid(fullname,0,1) eq '+' then begin
   fullname = state.calibpath + strmid(fname,1,strlen(fname)-1)
endif

IF NOT exists(fullname) THEN BEGIN
   MESSAGE, 'Flat ' + fullname + ' not found.' + bel, /INFO
   WIDGET_CONTROL, state.flatbase, SET_UVALUE=flats, /NO_COPY
   RETURN
ENDIF

;Use the calibrated image storage area as a temporary place to load the
;prospective flat. This is OK because the calibrated image storage area
;is always initialized before a processed image is displayed.
WIDGET_CONTROL, state.cimgptr, GET_UVALUE=flatframe, /NO_COPY

flatframe = readfits( fullname, /SILENT )
stat = SIZE( flatframe )
frank  = stat[ 0 ]
fxsize = stat[ 1 ]
fysize = stat[ 2 ]

;Put the storage pointer back.
WIDGET_CONTROL, state.cimgptr, SET_UVALUE=flatframe, /NO_COPY

IF frank NE 2 THEN BEGIN
   MESSAGE, 'Flat ' + fullname + ' is rank ' + STRING(frank,FORMAT='(G0.0)' ) + $
   '.  Must be 2.' + bel, /INFO
   RETURN
ENDIF

calval, state.xsize, fxsize, state.x1, state.x2, errflg, $
BANNER='LOAD FLAT: Error. Flat x-size is inconsistent with the ' + $
'established environment.', $
ISIZELAB='image frame x-size', $
CSIZELAB='calibration frame x-size', $
MLOWLAB='low limit of cropping region x-range', $
MHIGHLAB='high limit of cropping region x-range', $
DELTAMLAB='size of cropping region x-range'
err = errflg

calval, state.ysize, fysize, state.y1, state.y2, errflg, $
BANNER='LOAD FLAT: Error. Flat Y-size is inconsistent with the ' + $
'established environment.', $
ISIZELAB='image frame y-size', $
CSIZELAB='calibration frame y-size', $
MLOWLAB='low limit of cropping region y-range', $
MHIGHLAB='high limit of cropping region y-range', $
DELTAMLAB='size of cropping region y-range'
err = err OR errflg

IF err THEN BEGIN
   MESSAGE, '...Flat ' + fullname + ' not loaded.', /INFO
   WIDGET_CONTROL, state.flatbase, SET_UVALUE=flats, /NO_COPY
   RETURN
ENDIF

state.cxsize = fxsize
state.cysize = fysize

;Move the flat frame over to its proper location.
WIDGET_CONTROL, state.cimgptr, GET_UVALUE=tframe, /NO_COPY
WIDGET_CONTROL, flats[findex].flatid, GET_UVALUE=flatframe, /NO_COPY
flatframe = tframe
tframe = 0
WIDGET_CONTROL, state.cimgptr, SET_UVALUE=tframe, /NO_COPY
WIDGET_CONTROL, flats[findex].flatid, SET_UVALUE=flatframe, /NO_COPY

;Store the filename.
flats[findex].filename = fname

MESSAGE, filter + fullname + ' loaded.', /INFO

WIDGET_CONTROL, state.flatbase, SET_UVALUE=flats, /NO_COPY
END

; ------------------------------------------------------------------------------
; Procedure ccdphot_af
;   Add a new flat to the list of flats.
;   The storage list for the flats consists of a tree of base widgets. Flat
; frame arrays are stored in the user-values of these widgets. The root (Top
; Level Base) holds a scalar 1.0 and is the selected default if a filter
; code is not found in the list at process time.
;   If, upon entry, the filter code is not found in the current list, a new
; child base widget is added to the tree and a new entry is added to the
; flats list array (flats). The entries in this array are structures which
; identify the filter code, filename, and widget id of each of the flats.
;   If, upon entry, the filter code is found, the filename field of the flats
; entry is set to the incoming filename parameter. Finally, if the filename
; argument is non-null, ccdphot_lf (Load Flat) is called.
;   Note that, at this time, the new IDL storage mechanism called 'handles'
; has not been used. The reason is that there seems to be no 'automatic'
; method of releasing the storage in the event of a program 'crash.' The tree
; of widget bases will be destroyed, automatically, since the Top Level Base
; is always assigned a group leader (The Top Level Base of ccdphot).
; ------------------------------------------------------------------------------
PRO ccdphot_af, state, filtercode, fname

;Open access to the flats list array.
WIDGET_CONTROL, state.flatbase, GET_UVALUE=flats, /NO_COPY

;Search for the filtercode.
j = WHERE( flats[*].filtercode EQ filtercode, count )

IF count EQ 0 THEN BEGIN
   ;The incoming filter code is new.
   ;Add a new slot to the flats list array (flats) and update the list.
   flats = [ flats, flats[ 0 ] ]
   j = N_ELEMENTS( flats ) - 1
   flats[j].filtercode = filtercode
   flats[j].filename = fname
   flats[j].flatid = WIDGET_BASE( flats[0].flatid, UVALUE=1.0 )
ENDIF ELSE BEGIN
   ;The incoming filtercode is in the list. Set the filename field.
   IF fname NE '' THEN flats[j].filename = fname
ENDELSE

;Close access to the flats list array.
WIDGET_CONTROL, state.flatbase, SET_UVALUE=flats, /NO_COPY

IF fname NE '' THEN ccdphot_lf, state, j
END


; ------------------------------------------------------------------------------
; Procedure ccdphot_li
; Loads an image file into the image frame array.
; ------------------------------------------------------------------------------
PRO ccdphot_li, state, fname, $
                AUTOPHOT=autophot, PHOTTYPE=phottype, POSITION=position

bel = STRING( 7B )

IF NOT exists( fname ) THEN BEGIN
   MESSAGE, 'File ' + fname + ' not found.' + bel, /INFO
   WIDGET_CONTROL, state.imtextid, SET_VALUE=state.imfile
   RETURN
ENDIF

header = ''

;Use the calibrated image storage area as a temporary place to load the
;prospective image. This is OK because the calibrated image storage area
;is always initialized before a processed image is displayed.
CATCH, Error_status
IF Error_status NE 0 THEN BEGIN
   MESSAGE, 'Error loading ' + fname + bel, /INFO
   WIDGET_CONTROL, state.cimgptr, SET_UVALUE=tframe, /NO_COPY
   WIDGET_CONTROL, state.imtextid, SET_VALUE=state.imfile
   RETURN
ENDIF

WIDGET_CONTROL, state.cimgptr, GET_UVALUE=tframe, /NO_COPY
tframe = readfits( fname, header, /SILENT )
CATCH,/CANCEL

;Store the header in the user-value of the View Header button.
WIDGET_CONTROL, state.vheaderid, SET_UVALUE=header

;Initialize the new image parameters structure for the raw image.
it_init, tframe, im_parms

;Put the temporary storage pointer back.
WIDGET_CONTROL, state.cimgptr, SET_UVALUE=tframe, /NO_COPY

;Check for overscan error.
calval, im_parms.xsize, 0, state.xl, state.xr, errflg, $
BANNER='LOAD IMAGE: Error. Image x-size is inconsistent with the ' + $
'established overscan size or range.', $
ISIZELAB='image frame x-size', $
CSIZELAB='', $
MLOWLAB='low limit of overscan region', $
MHIGHLAB='high limit of overscan region', $
DELTAMLAB='size of overscan region'
err = errflg

;Check for x-range error.
calval, im_parms.xsize, state.cxsize, state.x1, state.x2, errflg, $
BANNER='LOAD IMAGE: Error. Image x-size is inconsistent with the ' + $
'established environment.', $
ISIZELAB='image frame x-size', $
CSIZELAB='calibration frame x-size', $
MLOWLAB='low limit of cropping region x-range', $
MHIGHLAB='high limit of cropping region x-range', $
DELTAMLAB='size of cropping region x-range'
err = err OR errflg

;Check for y-range error.
calval, im_parms.ysize, state.cysize, state.y1, state.y2, errflg, $
BANNER='LOAD IMAGE: Error. Image Y-size is inconsistent with the ' + $
'established environment.', $
ISIZELAB='image frame y-size', $
CSIZELAB='calibration frame y-size', $
MLOWLAB='low limit of cropping region y-range', $
MHIGHLAB='high limit of cropping region y-range', $
DELTAMLAB='size of cropping region y-range'
err = err OR errflg

IF err THEN BEGIN
   MESSAGE, '...Image ' + fname + ' cannot be loaded.', /INFO
   WIDGET_CONTROL, state.imtextid, SET_VALUE=state.imfile
   RETURN
ENDIF

;Move the newly loaded image to its permanent location.
WIDGET_CONTROL, state.cimgptr, GET_UVALUE=tframe, /NO_COPY
WIDGET_CONTROL, state.rimgptr, GET_UVALUE=rawframe, /NO_COPY
rawframe = tframe
tframe = 0
WIDGET_CONTROL, state.cimgptr, SET_UVALUE=tframe, /NO_COPY
WIDGET_CONTROL, state.rimgptr, SET_UVALUE=rawframe, /NO_COPY

state.imfile = fname
WIDGET_CONTROL, state.imtextid, SET_VALUE=state.imfile

state.nframes = im_parms.nframes
state.xsize   = im_parms.xsize
state.ysize   = im_parms.ysize

;Extract some info from the header, using the list of header correspondence
;names.
WIDGET_CONTROL, state.hdrlistptr, GET_UVALUE=hdrlist, /NO_COPY
parsekey,header,hdrlist,hdrinfo
WIDGET_CONTROL, state.hdrlistptr, SET_UVALUE=hdrlist, /NO_COPY

;Copy the header imformation to the image parameters structure.
im_parms.airmass  = hdrinfo.airmass
im_parms.date     = hdrinfo.date
im_parms.expdelta = hdrinfo.expdelta
im_parms.exptime  = hdrinfo.exptime
im_parms.imfile   = hdrinfo.imfile
im_parms.filter   = hdrinfo.filter
im_parms.object   = hdrinfo.object
im_parms.ut       = hdrinfo.ut
im_parms.jd       = hdrinfo.jd

im_parms.autophot = 0

IF im_parms.imfile EQ '' THEN im_parms.imfile='(No name)'

;phot_parms.objnum = 0

ccdphot_af, state, im_parms.filter, ''

;Refresh the flats list widget.
WIDGET_CONTROL, state.flatbase, GET_UVALUE=flats, /NO_COPY
WIDGET_CONTROL, state.flatlistid, SET_VALUE=ccdphot_fmt(flats)
WIDGET_CONTROL, state.flatbase, SET_UVALUE=flats, /NO_COPY

;Freeze the flat filename edit widgets.
WIDGET_CONTROL, state.flatpickid, SENSITIVE=0
WIDGET_CONTROL, state.flattextid, SENSITIVE=0, SET_VALUE=''

;Put the new image parameters away.
WIDGET_CONTROL, state.imparmsptr, SET_UVALUE=im_parms

ccdphot_pr, state
ccdphot_di, state, AUTOPHOT=autophot, PHOTTYPE=phottype, POSITION=position
END


; ------------------------------------------------------------------------------
; Procedure ccdphot_lb
; Loads a bias frame into memory.
; ------------------------------------------------------------------------------
PRO ccdphot_lb, state, fname
bel = STRING( 7B )

fullname=fname
if strmid(fullname,0,1) eq '+' then begin
   fullname = state.calibpath + strmid(fname,1,strlen(fname)-1)
endif

IF NOT exists( fullname ) THEN BEGIN
   WIDGET_CONTROL, state.biastextid, SET_VALUE=state.biasname
   MESSAGE, 'File ' + fullname + ' not found.' + bel, /INFO
   RETURN
ENDIF

;Use the calibrated image storage area as a temporary place to load the
;prospective bias frame. This is OK because the calibrated image storage area
;is always initialized before a processed image is displayed.
WIDGET_CONTROL, state.cimgptr, GET_UVALUE=tframe, /NO_COPY
tframe = readfits( fullname, /SILENT )
stat = SIZE( tframe )
bxsize = stat[ 1 ]
bysize = stat[ 2 ]

;Put the temporary storage pointer back.
WIDGET_CONTROL, state.cimgptr, SET_UVALUE=tframe, /NO_COPY

calval, state.xsize, bxsize, state.x1, state.x2, errflg, $
BANNER='LOAD BIAS: Error. Bias x-size is inconsistent with the ' + $
       'established environment.', $
ISIZELAB='image frame x-size', $
CSIZELAB='calibration frame x-size', $
MLOWLAB='low limit of cropping region x-range', $
MHIGHLAB='high limit of cropping region x-range', $
DELTAMLAB='size of cropping region x-range'
err = errflg

calval, state.ysize, bysize, state.y1, state.y2, errflg, $
BANNER='LOAD BIAS: Error. Bias Y-size is inconsistent with the ' + $
'established environment.', $
ISIZELAB='image frame y-size', $
CSIZELAB='calibration frame y-size', $
MLOWLAB='low limit of cropping region y-range', $
MHIGHLAB='high limit of cropping region y-range', $
DELTAMLAB='size of cropping region y-range'
err = err OR errflg

IF err THEN BEGIN
   MESSAGE, '...Bias frame ' + fullname + ' cannot be loaded.', /INFO
   RETURN
ENDIF

;Move the newly loaded bias frame to its permanent location.
WIDGET_CONTROL, state.cimgptr, GET_UVALUE=tframe, /NO_COPY
WIDGET_CONTROL, state.biasptr, GET_UVALUE=biasframe, /NO_COPY
biasframe = tframe
tframe = 0
WIDGET_CONTROL, state.cimgptr, SET_UVALUE=tframe, /NO_COPY
WIDGET_CONTROL, state.biasptr, SET_UVALUE=biasframe, /NO_COPY

state.biasname = fname
WIDGET_CONTROL, state.biastextid, SET_VALUE=fname

state.cxsize = bxsize
state.cysize = bysize

MESSAGE, 'Bias frame ' + fullname + ' loaded.', /INFO

END

; ------------------------------------------------------------------------------
; Procedure ccdphot_ld
; Loads a dark frame into memory.
; ------------------------------------------------------------------------------
PRO ccdphot_ld, state, fname
bel = STRING( 7B )

IF fname EQ '[none]' OR fname EQ '' THEN BEGIN
	WIDGET_CONTROL, state.darkptr, GET_UVALUE=darkframe, /NO_COPY
	darkframe = 0
	WIDGET_CONTROL, state.darkptr, SET_UVALUE=darkframe, /NO_COPY
	state.darkname = fname
	WIDGET_CONTROL, state.darktextid, SET_VALUE=fname
	MESSAGE, 'Dark frame calibration disabled.', /INFO

ENDIF ELSE BEGIN

	fullname=fname
	if strmid(fullname,0,1) eq '+' then begin
	   fullname = state.calibpath + strmid(fname,1,strlen(fname)-1)
	endif

	IF NOT exists( fullname ) THEN BEGIN
	   WIDGET_CONTROL, state.darktextid, SET_VALUE=state.darkname
	   MESSAGE, 'File ' + fullname + ' not found.' + bel, /INFO
	   RETURN
	ENDIF

	;Use the calibrated image storage area as a temporary place to load the
	;prospective dark frame. This is OK because the calibrated image storage area
	;is always initialized before a processed image is displayed.
	WIDGET_CONTROL, state.cimgptr, GET_UVALUE=tframe, /NO_COPY
	tframe = readfits( fullname, /SILENT )
	stat = SIZE( tframe )
	bxsize = stat[ 1 ]
	bysize = stat[ 2 ]

	;Put the temporary storage pointer back.
	WIDGET_CONTROL, state.cimgptr, SET_UVALUE=tframe, /NO_COPY

	calval, state.xsize, bxsize, state.x1, state.x2, errflg, $
	BANNER='LOAD DARK: Error. Dark x-size is inconsistent with the ' + $
	'established environment.', $
	ISIZELAB='image frame x-size', $
	CSIZELAB='calibration frame x-size', $
	MLOWLAB='low limit of cropping region x-range', $
	MHIGHLAB='high limit of cropping region x-range', $
	DELTAMLAB='size of cropping region x-range'
	err = errflg

	calval, state.ysize, bysize, state.y1, state.y2, errflg, $
	BANNER='LOAD DARK: Error. Dark Y-size is inconsistent with the ' + $
	'established environment.', $
	ISIZELAB='image frame y-size', $
	CSIZELAB='calibration frame y-size', $
	MLOWLAB='low limit of cropping region y-range', $
	MHIGHLAB='high limit of cropping region y-range', $
	DELTAMLAB='size of cropping region y-range'
	err = err OR errflg

	IF err THEN BEGIN
	   MESSAGE, '...Dark frame ' + fullname + ' cannot be loaded.', /INFO
	   RETURN
	ENDIF

	;Move the newly loaded dark frame to its permanent location.
	WIDGET_CONTROL, state.cimgptr, GET_UVALUE=tframe, /NO_COPY
	WIDGET_CONTROL, state.darkptr, GET_UVALUE=darkframe, /NO_COPY
	darkframe = tframe
	tframe = 0
	WIDGET_CONTROL, state.cimgptr, SET_UVALUE=tframe, /NO_COPY
	WIDGET_CONTROL, state.darkptr, SET_UVALUE=darkframe, /NO_COPY

	state.darkname = fname
	WIDGET_CONTROL, state.darktextid, SET_VALUE=fname

	state.cxsize = bxsize
	state.cysize = bysize

	MESSAGE, 'Dark frame ' + fullname + ' loaded.', /INFO

ENDELSE

END

; ------------------------------------------------------------------------------
; Procedure ccdphot_lc
; Loads calibration files, based upon the contents of a calibration
; environment file.
; ------------------------------------------------------------------------------
PRO ccdphot_lc, state
bel = STRING( 7B )

IF state.calibfile EQ '' THEN RETURN

IF NOT exists( state.calibpath+state.calibfile ) THEN BEGIN
   MESSAGE, 'Calibration Environment File ' + $
      state.calibpath + state.calibfile + ' not found.'+bel, /INFO
   WIDGET_CONTROL, state.caltextid, SET_VALUE=''
   RETURN
ENDIF

; Remember the value of the calibration environment save flag.
calibflag = state.calibflag

IF state.calibflag THEN BEGIN
   t = [ '  Calibration environment changes may not have been saved.', $
   '  If you do not wish to lose any changes, cancel this operation and', $
   'save the current environment.' ]
   con = qannounc( t, TITLE='Calibration Environment Save Confirmation', $
   FALSE='Cancel', TRUE='Ok, continue', XSIZE=70 )
   IF NOT con THEN RETURN
   calibflag = 0
ENDIF

WIDGET_CONTROL, state.flatbase, GET_UVALUE=flats, /NO_COPY
flatbase1 = WIDGET_INFO( flats[0].flatid, /CHILD )
WIDGET_CONTROL, state.flatbase, SET_UVALUE=flats, /NO_COPY

IF flatbase1 NE 0L THEN BEGIN
   t = [ '  You may choose to ADD to the existing list of flat fields, or', $
         'to PURGE the existing list.' ]
   purgelist=qannounc( t, TITLE='Calibration Environment Load Confirmation', $
   FALSE='ADD to the existing list', TRUE='PURGE the existing list', XSIZE=70 )
   calibflag = 1
   IF purgelist THEN BEGIN
      WIDGET_CONTROL, state.flatbase, GET_UVALUE=flats, /NO_COPY
      WIDGET_CONTROL, flats[0].flatid, /DESTROY
      flats = [flats[0]]
      flats[0].flatid = WIDGET_BASE( UVALUE=1.0, GROUP_LEADER=state.mainbase )
      calibflag = 0
      WIDGET_CONTROL, state.flatbase, SET_UVALUE=flats, /NO_COPY
   ENDIF
ENDIF

GET_LUN, lu
OPENR, lu, state.calibpath+state.calibfile, ERROR=err
IF err NE 0 THEN BEGIN
   ; We really have a problem.
   FREE_LUN, lu
   MESSAGE, !error_state.msg + bel, /INFO
   RETURN
ENDIF

line = ''
READF, lu, line
linebuf = [ line ]
WHILE NOT EOF( lu ) DO BEGIN
   READF, lu, line
   linebuf = [ linebuf, line ]
ENDWHILE
CLOSE, lu
FREE_LUN, lu

; First line. Check for valid version.
w = WHERE( state.okver EQ linebuf[0], count )
IF count EQ 0 THEN BEGIN
   MESSAGE, 'Unknown file version ' + line + bel, /INFO
   RETURN
ENDIF

; ------------------------------------------------------------------------------
;Here is where the conversion from the old format takes place, if necessary.
;
IF linebuf[0] EQ state.okver[0] THEN BEGIN
   n = N_ELEMENTS( linebuf )
   CASE STRUPCASE( linebuf[1] ) OF
      'LCCD' : BEGIN
         line1 = '-1 -1'
         line2 = '  -1  -1  -1  -1'
         linebuf = [ state.okver[1], line1, line2, linebuf[2:n-1] ]
      END
      'LORAL' : BEGIN
         line1 = linebuf[2]
         line2 = linebuf[3]
         linebuf = [ state.okver[1], line1, line2, linebuf[4:n-1] ]
      END
      ELSE : BEGIN
         MESSAGE, 'Error. Unable to interpret old format environment file.'+ $
         bel, /INFO
         RETURN
      END
   ENDCASE
   MESSAGE, 'Old format environment file successfully processed.', /INFO
ENDIF
; ------------------------------------------------------------------------------

error = 0

; Second line, overscan region.
overscan = linebuf[1]
n1 = GETTOK( overscan, ' ' )
n2 = GETTOK( overscan, ' ' )
xl = FIX( n1 )
xr = FIX( n2 )
calval, state.xsize, 0, xl, xr, errflg, $
BANNER='LOAD CALIB: Error. Specified Overscan is inconsistent with the ' + $
'established environment.', $
ISIZELAB='image frame x-size', $
CSIZELAB='', $
MLOWLAB='low limit of overscan region', $
MHIGHLAB='high limit of overscan region', $
DELTAMLAB='size of overscan region'

IF NOT errflg THEN BEGIN
   state.xl = xl
   state.xr = xr
   overscan = STRING( xl, ':', xr, FORMAT='(G0.0,A,G0.0)' )
   state.overscan = overscan
   WIDGET_CONTROL, state.overscanid, SET_VALUE=overscan
ENDIF

;Third line, cropping region.
cropreg = linebuf[2]
n1 = GETTOK( cropreg, ' ' )
n2 = GETTOK( cropreg, ' ' )
n3 = GETTOK( cropreg, ' ' )
n4 = GETTOK( cropreg, ' ' )

x1 = FIX( n1 )
x2 = FIX( n2 )

calval, state.xsize, state.cxsize, x1, x2, errflg, $
BANNER='LOAD CALIB: Error. Specified X-range is inconsistent with the ' + $
'established environment.', $
ISIZELAB='image frame x-size', $
CSIZELAB='calibration frame x-size', $
MLOWLAB='low limit of cropping region x-range', $
MHIGHLAB='high limit of cropping region x-range', $
DELTAMLAB='size of cropping region x-range'

IF NOT errflg THEN BEGIN
   state.x1 = x1
   state.x2 = x2
   xrange = STRING( x1, ':', x2, FORMAT='(G0.0,A,G0.0)' )
   state.xrange = xrange
   WIDGET_CONTROL, state.xrangeid, SET_VALUE=xrange
ENDIF

y1 = FIX( n3 )
y2 = FIX( n4 )
calval, state.ysize, state.cysize, y1, y2, errflg, $
BANNER='LOAD CALIB: Error. Specified Y-range is inconsistent with the ' + $
'established environment.', $
ISIZELAB='image frame y-size', $
CSIZELAB='calibration frame y-size', $
MLOWLAB='low limit of cropping region y-range', $
MHIGHLAB='high limit of cropping region y-range', $
DELTAMLAB='size of cropping region y-range'

IF NOT errflg THEN BEGIN
   state.y1 = y1
   state.y2 = y2
   yrange = STRING( y1, ':', y2, FORMAT='(G0.0,A,G0.0)' )
   state.yrange = yrange
   WIDGET_CONTROL, state.yrangeid, SET_VALUE=yrange
ENDIF

;Fourth line, Bias frame.
ccdphot_lb, state, linebuf[3]

; Fifth line (maybe), Dark frame.
IF linebuf[0] EQ state.okver[1] THEN BEGIN
   ccdphot_ld, state, '[none]'
   nextline = 4
ENDIF ELSE BEGIN
   ccdphot_ld, state, linebuf[4]
   nextline = 5
ENDELSE

;Load the flat frames.
FOR j=nextline, N_ELEMENTS(linebuf)-1 DO BEGIN
   flatline = linebuf[j]
   filter = GETTOK( flatline, ' ' )
   fname  = GETTOK( flatline, ' ' )
	fullname=fname
	if strmid(fullname,0,1) eq '+' then begin
	   fullname = state.calibpath + strmid(fname,1,strlen(fname)-1)
	endif
   IF exists( fullname ) THEN BEGIN
      ccdphot_af, state, filter, fname
   ENDIF ELSE BEGIN
      MESSAGE, 'File ' + fullname + ' not found.' + bel, /INFO
   ENDELSE
ENDFOR

;Set calibration environment save flag.
state.calibflag = calibflag

;Update the flats list widget.
WIDGET_CONTROL, state.flatbase, GET_UVALUE=flats, /NO_COPY
WIDGET_CONTROL, state.flatlistid, SET_VALUE=ccdphot_fmt(flats)
WIDGET_CONTROL, state.flatbase, SET_UVALUE=flats, /NO_COPY

;Freeze the filename edit widgets.
WIDGET_CONTROL, state.flatpickid, SENSITIVE=0
WIDGET_CONTROL, state.flattextid, SENSITIVE=0, SET_VALUE=''
state.fselected = 0

;Process and display the image (if loaded).
ccdphot_pr, state
ccdphot_di, state
END


; ------------------------------------------------------------------------------
; Procedure ccdphot_sc
; Saves the current calibration environment to a file.
; ------------------------------------------------------------------------------
PRO ccdphot_sc, state
bel = STRING( 7B )

IF state.calibfile EQ '' THEN RETURN

WIDGET_CONTROL, state.flatbase, GET_UVALUE=flats, /NO_COPY
nflats = N_ELEMENTS( flats ) - 1
WIDGET_CONTROL, state.flatbase, SET_UVALUE=flats, /NO_COPY

notok = (state.biasname EQ '') OR (nflats EQ 0)

IF notok THEN BEGIN
   MESSAGE, 'Error. Bias, and at least one flat must be loaded.' + $
   bel, /INFO
   RETURN
ENDIF

xl = state.xl
xr = state.xr

x1 = state.x1
x2 = state.x2

y1 = state.y1
y2 = state.y2

IF exists( state.calibpath+state.calibfile ) THEN BEGIN
   t = $
   [ '  Calibration environment file ' + state.calibpath+state.calibfile + ' exists.', $
     '  It will be overwritten, unless you choose to cancel and change', $
     'the name of the file.' ]
   con = qannounc( t, TITLE='File Overwrite Confirmation', $
   FALSE='Cancel', TRUE='Overwrite File', XSIZE=70 )
   IF NOT con THEN RETURN
ENDIF

GET_LUN, lu
OPENW, lu, state.calibpath+state.calibfile, ERROR=err
IF err NE 0 THEN BEGIN
   ; Something is wrong.
   MESSAGE, !error_state.msg + STRING(7B), /INFO
   RETURN
ENDIF

PRINTF, lu, state.okver[2]
PRINTF, lu, xl, xr, FORMAT='(2I5)'

PRINTF, lu, x1, x2, y1, y2, FORMAT='(4I5)'
PRINTF, lu, state.biasname
IF state.darkname eq '' then $
   PRINTF, lu, '[none]' $
ELSE $
   PRINTF, lu, state.darkname

WIDGET_CONTROL, state.flatbase, GET_UVALUE=flats, /NO_COPY
FOR j=1, N_ELEMENTS(flats)-1 DO BEGIN
   PRINTF, lu, flats[j].filtercode + ' ' +  flats[j].filename
ENDFOR
WIDGET_CONTROL, state.flatbase, SET_UVALUE=flats, /NO_COPY

CLOSE, lu
FREE_LUN, lu
state.calibflag = 0
MESSAGE, 'Calibration environment saved to file ' + $
          state.calibpath+state.calibfile, /INFO
END


;<<<<<<<<<<<<<<<<<<<<<<<<< EVENT PROCESSING <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; ------------------------------------------------------------------------------
; Procedure ccdphot_it_eve
; Event handler for the cw_itool Top Level Base.
; This procedure follows my method for handling events from alternate Top
;Level Bases defined within a widget application. The user-value of the TLB
;contains pointers that allow the handler to access other data that are
;maintained in the host application.
; At this time, the only event arriving from this base is the exit event
;generated by cw_itool. The event has the following structure:
;      {id:0L, top:0L, handler:0L, phedit:0B, tpedit:0B}
; ------------------------------------------------------------------------------
PRO ccdphot_it_eve, event
WIDGET_CONTROL, event.id, /HOURGLASS

bel = STRING( 7B )

;Get the TLB state structure.
WIDGET_CONTROL, event.top, GET_UVALUE=topstate, /NO_COPY

;Use the pointer to access the main state of ccdphot.
WIDGET_CONTROL, topstate.mainstash, GET_UVALUE=state, /NO_COPY

tmsg = [ '\\\\\\ WARNING //////' ]
 
IF event.phedit THEN BEGIN
   tmsg = [ tmsg, $
   'PHOTOMETRY parameter changes have not been saved.' ]
ENDIF
 
IF event.tpedit THEN BEGIN
   tmsg = [ tmsg, 'TEMPLATE changes have not been saved.' ]
ENDIF
 
tmsg = [ tmsg, 'Do you wish to quit Itool without saving these changes?' ]
con = 1
 
IF event.phedit OR event.tpedit THEN BEGIN
   con = qannounc( tmsg, TITLE='Itool Exit Confirmation', YSIZE=6 )
ENDIF
 
IF con THEN BEGIN
   cwitoolid = WIDGET_INFO( state.cwitoolbase, /CHILD )
   WIDGET_CONTROL, state.cwitoolbase, MAP=0
   WIDGET_CONTROL, cwitoolid, /DESTROY
ENDIF

WIDGET_CONTROL, topstate.mainstash, SET_UVALUE=state, /NO_COPY
WIDGET_CONTROL, event.top, SET_UVALUE=topstate, /NO_COPY
END


; ------------------------------------------------------------------------------
; Procedure ccdphot_edit_eve
; Event handler for Calibration Environment Parameters.
; This procedure follows my method for handling events from alternate Top
;Level Bases defined within a widget application. The user-value of the TLB
;contains pointers that allow the handler to access other data that are
;maintained in the host application.
; ------------------------------------------------------------------------------
PRO ccdphot_edit_eve, event
WIDGET_CONTROL, event.id, /HOURGLASS
bel = STRING( 7B )

;Get the TLB state structure.
WIDGET_CONTROL, event.top, GET_UVALUE=topstate, /NO_COPY

;Use the pointer to access the main state of ccdphot.
WIDGET_CONTROL, topstate.mainstash, GET_UVALUE=state, /NO_COPY

CASE event.id OF
   state.biaspickid : BEGIN
      IF state.calibpath NE '' THEN BEGIN
         f = DIALOG_PICKFILE( GROUP=event.top, TITLE='Select Bias Frame', $
             PATH=state.calibpath )
      ENDIF ELSE BEGIN
         f = DIALOG_PICKFILE( GROUP=event.top, TITLE='Select Bias Frame' )
      ENDELSE
      fn = STRTRIM( f, 2 )
      IF fn NE '' THEN BEGIN
         ccdphot_lb, state, fn
         ccdphot_pr, state
         ccdphot_di, state
         state.calibflag = 1
      ENDIF
   END

   state.darkpickid : BEGIN
      IF state.calibpath NE '' THEN BEGIN
         f = DIALOG_PICKFILE( GROUP=event.top, TITLE='Select Dark Frame', $
             PATH=state.calibpath )
      ENDIF ELSE BEGIN
         f = DIALOG_PICKFILE( GROUP=event.top, TITLE='Select Dark Frame' )
      ENDELSE
      fn = STRTRIM( f, 2 )
      IF fn NE '' THEN BEGIN
         ccdphot_ld, state, fn
         ccdphot_pr, state
         ccdphot_di, state
         state.calibflag = 1
      ENDIF
   END

   state.biastextid : BEGIN
      WIDGET_CONTROL, event.id, GET_VALUE=value
      fn = STRTRIM( value[0], 2 )
      IF fn NE '' THEN BEGIN
         ccdphot_lb, state, fn
         ccdphot_pr, state
         ccdphot_di, state
         state.calibflag = 1
      ENDIF ELSE BEGIN
         state.biasname = ''
         WIDGET_CONTROL, state.biasptr, GET_UVALUE=biasframe, /NO_COPY
         biasframe = 0
         WIDGET_CONTROL, state.biasptr, SET_UVALUE=biasframe, /NO_COPY
         MESSAGE, 'Bias frame set to zero (no bias).', /INFO
      ENDELSE
   END

   state.darktextid : BEGIN
      WIDGET_CONTROL, event.id, GET_VALUE=value
      fn = STRTRIM( value[0], 2 )
      IF fn NE '' THEN BEGIN
         ccdphot_ld, state, fn
         ccdphot_pr, state
         ccdphot_di, state
         state.calibflag = 1
      ENDIF ELSE BEGIN
         state.darkname = ''
         WIDGET_CONTROL, state.darkptr, GET_UVALUE=darkframe, /NO_COPY
         darkframe = 0
         WIDGET_CONTROL, state.darkptr, SET_UVALUE=darkframe, /NO_COPY
         MESSAGE, 'Dark frame set to zero (no dark).', /INFO
      ENDELSE
   END

   state.dismissid : BEGIN
      WIDGET_CONTROL, state.editbase, MAP=0
   END

   state.flatlistid : BEGIN
      selected = event.index + 1
      state.fselected = selected
      WIDGET_CONTROL, state.flatpickid, SENSITIVE=1
      WIDGET_CONTROL, state.flatbase, GET_UVALUE=flats, /NO_COPY
      WIDGET_CONTROL, state.flattextid, SET_VALUE=flats[selected].filename, $
                      SENSITIVE=1
      WIDGET_CONTROL, state.flatbase, SET_UVALUE=flats, /NO_COPY
   END

   state.flatpickid : BEGIN
      WIDGET_CONTROL, state.flatbase, GET_UVALUE=flats, /NO_COPY
      title = 'Select Flat Frame for Filter ' + $
              flats[state.fselected].filtercode
      WIDGET_CONTROL, state.flatbase, SET_UVALUE=flats, /NO_COPY

      IF state.calibpath NE '' THEN BEGIN
         f = DIALOG_PICKFILE(GROUP=event.top, TITLE=title, PATH=state.calibpath)
      ENDIF ELSE BEGIN
         f = DIALOG_PICKFILE( GROUP=event.top, TITLE=title )
      ENDELSE

      fn = STRTRIM( f, 2 )

      IF fn NE '' THEN BEGIN
         WIDGET_CONTROL, state.flatbase, GET_UVALUE=flats, /NO_COPY
         flats[state.fselected].filename = fn

         WIDGET_CONTROL, state.flattextid, SET_VALUE=fn
         WIDGET_CONTROL, state.flatlistid, SET_VALUE=ccdphot_fmt(flats), $
                         SET_LIST_SELECT=state.fselected-1
         WIDGET_CONTROL, state.flatbase, SET_UVALUE=flats, /NO_COPY

         ccdphot_lf, state, state.fselected
         ccdphot_pr, state
         ccdphot_di, state
         state.calibflag = 1
      ENDIF

   END

   state.flattextid : BEGIN
      WIDGET_CONTROL, event.id, GET_VALUE=value
      fn = STRTRIM( value[0], 2 )
		fullname=fn
		if strmid(fullname,0,1) eq '+' then begin
		   fullname = state.calibpath + strmid(fn,1,strlen(fn)-1)
		endif
      IF fullname NE '' THEN BEGIN
         IF exists( fullname ) THEN BEGIN
            WIDGET_CONTROL, state.flatbase, GET_UVALUE=flats, /NO_COPY
            flats[state.fselected].filename = fn
            WIDGET_CONTROL, state.flatlistid, SET_VALUE=ccdphot_fmt(flats), $
               SET_LIST_SELECT=state.fselected-1
            WIDGET_CONTROL, state.flatbase, SET_UVALUE=flats, /NO_COPY
            ccdphot_lf, state, state.fselected
            ccdphot_pr, state
            ccdphot_di, state
            state.calibflag = 1
         ENDIF ELSE BEGIN
            WIDGET_CONTROL, state.flatbase, GET_UVALUE=flats, /NO_COPY
            MESSAGE, 'File '+fn+' does not exist.'+bel, /INFO
            WIDGET_CONTROL, event.id, $
                            SET_VALUE=flats[state.fselected].filename
            WIDGET_CONTROL, state.flatbase, SET_UVALUE=flats, /NO_COPY
         ENDELSE
      ENDIF 
   END

   state.overscanid : BEGIN
      WIDGET_CONTROL, event.id, GET_VALUE=value
      v = STRTRIM( value[0], 2 )
      overscan = v

      IF v EQ '' THEN BEGIN
         state.xl = -1
         state.xr = -1
         state.overscan = ''
         state.calibflag = 1
         WIDGET_CONTROL, topstate.mainstash, SET_UVALUE=state, /NO_COPY
         WIDGET_CONTROL, event.top, SET_UVALUE=topstate, /NO_COPY
         RETURN
      ENDIF

      n1 = GETTOK( v, ':' )
      n2 = GETTOK( v, ' ' )
      xl = FIX( n1 )
      xr = FIX( n2 )

      calval, state.xsize, 0, xl, xr, errflg, $
      BANNER='OVERSCAN: Error. Specified overscan is inconsistent with the ' + $
      'established environment.', $
      ISIZELAB='image frame x-size', $
      CSIZELAB='', $
      MLOWLAB='low limit of overscan region', $
      MHIGHLAB='high limit of overscan region', $
      DELTAMLAB='size of overscan region'

      IF errflg THEN BEGIN
         WIDGET_CONTROL, state.overscanid, SET_VALUE=state.overscan
         WIDGET_CONTROL, topstate.mainstash, SET_UVALUE=state, /NO_COPY
         WIDGET_CONTROL, event.top, SET_UVALUE=topstate, /NO_COPY
         RETURN
      ENDIF

      state.overscan = overscan
      state.xl = xl
      state.xr = xr

      IF state.processflag THEN BEGIN
         ccdphot_pr, state
         ccdphot_di, state
      ENDIF

      state.calibflag = 1
   END

   state.xrangeid : BEGIN
      WIDGET_CONTROL, event.id, GET_VALUE=value
      xrange = STRTRIM( value[0], 2 )
      IF (xrange EQ '') OR (xrange EQ '*') THEN BEGIN
         xrange = '*'
         x1 = -1
         x2 = -1
      ENDIF ELSE BEGIN
         v = xrange
         n1 = GETTOK( v, ':' )
         n2 = GETTOK( v, ' ' )
         x1 = FIX( n1 )
         x2 = FIX( n2 )
      ENDELSE

      calval, state.xsize, state.cxsize, x1, x2, errflg, $
      BANNER='X-RANGE: Error. Specified X-range is inconsistent with the ' + $
      'established environment.', $
      ISIZELAB='image frame x-size', $
      CSIZELAB='calibration frame x-size', $
      MLOWLAB='low limit of cropping region x-range', $
      MHIGHLAB='high limit of cropping region x-range', $
      DELTAMLAB='size of cropping region x-range'

      IF errflg THEN BEGIN
         WIDGET_CONTROL, state.xrangeid, SET_VALUE=state.xrange
         WIDGET_CONTROL, topstate.mainstash, SET_UVALUE=state, /NO_COPY
         WIDGET_CONTROL, event.top, SET_UVALUE=topstate, /NO_COPY
         RETURN
      ENDIF

      state.x1 = x1
      state.x2 = x2
      state.xrange = xrange

      IF state.processflag THEN BEGIN
         ccdphot_pr, state
         ccdphot_di, state
      ENDIF

      state.calibflag = 1
   END

   state.yrangeid : BEGIN
      WIDGET_CONTROL, event.id, GET_VALUE=value
      yrange = STRTRIM( value[0], 2 )
      IF (yrange EQ '') OR (yrange EQ '*') THEN BEGIN
         yrange = '*'
         y1 = -1
         y2 = -1
      ENDIF ELSE BEGIN
         v = yrange
         n1 = GETTOK( v, ':' )
         n2 = GETTOK( v, ' ' )
         y1 = FIX( n1 )
         y2 = FIX( n2 )
      ENDELSE

      calval, state.ysize, state.cysize, y1, y2, errflg, $
      BANNER='Y-RANGE: Error. Specified Y-range is inconsistent with the ' + $
      'established environment.', $
      ISIZELAB='image frame y-size', $
      CSIZELAB='calibration frame y-size', $
      MLOWLAB='low limit of cropping region y-range', $
      MHIGHLAB='high limit of cropping region y-range', $
      DELTAMLAB='size of cropping region y-range'

      IF errflg GT 0 THEN BEGIN
         WIDGET_CONTROL, state.yrangeid, SET_VALUE=state.yrange
         WIDGET_CONTROL, topstate.mainstash, SET_UVALUE=state, /NO_COPY
         WIDGET_CONTROL, event.top, SET_UVALUE=topstate, /NO_COPY
         RETURN
      ENDIF

      state.y1 = y1
      state.y2 = y2
      state.yrange = yrange

      IF state.processflag THEN BEGIN
         ccdphot_pr, state
         ccdphot_di, state
      ENDIF

      state.calibflag = 1
   END

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

WIDGET_CONTROL, topstate.mainstash, SET_UVALUE=state, /NO_COPY
WIDGET_CONTROL, event.top, SET_UVALUE=topstate, /NO_COPY
END


; ------------------------------------------------------------------------------
; Procedure ccdphot_eve
; Main event handler for ccdphot (main buttons).
; ------------------------------------------------------------------------------
PRO ccdphot_eve, event
bel = STRING( 7B )
WIDGET_CONTROL, event.id, /HOURGLASS

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

CASE event.id OF
   ;Calibration environment file events.
   state.calpickid : BEGIN
      f = DIALOG_PICKFILE( GROUP=event.top, TITLE='Select Calibration File', $
          PATH=state.calibpath )
      fn = STRTRIM( f, 2 )
      IF fn NE '' THEN BEGIN
         state.calibfile = fn
         WIDGET_CONTROL, state.caltextid, SET_VALUE=fn
      ENDIF
   END

   state.calloadid : BEGIN
      ccdphot_lc, state
   END

   state.calsaveid : BEGIN
      ccdphot_sc, state
   END

   state.caltextid : BEGIN
      WIDGET_CONTROL, event.id, GET_VALUE=value
      f = STRTRIM( value[0], 2 )
      state.calibfile = f
      MESSAGE, 'Calibration file set to ' + f, /INFO
   END

   state.editid : BEGIN
      WIDGET_CONTROL, state.editbase, /REALIZE, /MAP
   END

   ;Main button widgets.
   state.exitid : BEGIN

      valid = WIDGET_INFO( state.cwitoolbase, /VALID_ID )

      IF valid THEN BEGIN
         WIDGET_CONTROL, state.cwitoolid, GET_VALUE=status
      ENDIF ELSE BEGIN
         status = {msg:''}
      ENDELSE

      tmsg = ['']
      IF state.calibflag THEN BEGIN
         tmsg = [ tmsg, $
         'Calibration environment changes have not been saved.' ]
      ENDIF

      tmsg = [ tmsg, '****************************************', $
      'Do you really wish to quit CCDPHOT?' ]

      con = qannounc( [status.msg,tmsg], TITLE=' CCDPHOT Exit Confirmation', $
            XSIZE=60, YSIZE=6 )
      IF con THEN BEGIN
         ;Bye.
         WIDGET_CONTROL, stash, SET_UVALUE=state, /NO_COPY
         WIDGET_CONTROL, event.top, /DESTROY
         RETURN
      ENDIF
   END

   ;Image file events.
   state.impickid : BEGIN
print,'CCDPHOT: ',state.impath
      IF state.impath NE '' THEN BEGIN
         f = DIALOG_PICKFILE( GROUP=event.top, TITLE='Select Image Frame', $
             PATH=state.impath )
      ENDIF ELSE BEGIN
         f = DIALOG_PICKFILE( GROUP=event.top, TITLE='Select Image Frame' )
      ENDELSE
      fn = STRTRIM( f, 2 )
      IF fn NE '' THEN BEGIN
         WIDGET_CONTROL, state.imtextid, SET_VALUE=fn
         ccdphot_li, state, fn
      ENDIF
   END

   state.imtextid : BEGIN
      WIDGET_CONTROL, event.id, GET_VALUE=value
      fn = STRTRIM( value[0], 2 )
      IF fn NE '' THEN BEGIN
         ccdphot_li, state, fn
      ENDIF ELSE BEGIN
         IF state.imfile NE '' THEN BEGIN
            WIDGET_CONTROL, state.imtextid, SET_VALUE=state.imfile
         ENDIF
      ENDELSE
   END

   state.rawproid : BEGIN
      ;Raw/Processed toggle event.
      state.processflag = event.value
      ccdphot_pr, state
      ccdphot_di, state
   END

   state.nextid : BEGIN
      IF state.imfile NE '' THEN BEGIN
         f = state.imfile
         l = STRLEN( f )
         ext = STRMID( f, l-3, 3 )
         bext = BYTE( ext )
         t = WHERE( bext LT 48 AND bext GT 57, count )
         IF count EQ 0 THEN BEGIN
            next = STRING( FIX( ext ) + 1, FORMAT='(I3.3)' )
            STRPUT, f, next, l-3
            ccdphot_li, state, f
            IF state.imfile EQ f THEN BEGIN
               WIDGET_CONTROL, state.imtextid, SET_VALUE=f
            ENDIF
         ENDIF
      ENDIF ELSE BEGIN
         MESSAGE, 'Error. No image.' + bel, /INFO
      ENDELSE
   END

   state.previd : BEGIN
      IF state.imfile NE '' THEN BEGIN
         f = state.imfile
         l = STRLEN( f )
         ext = STRMID( f, l-3, 3 )
         bext = BYTE( ext )
         t = WHERE( bext LT 48 AND bext GT 57, count )
         IF count EQ 0 THEN BEGIN
            newnum = FIX( ext ) - 1
            IF newnum GE 0 THEN BEGIN
               next = STRING( newnum, FORMAT='(I3.3)' )
               STRPUT, f, next, l-3
               ccdphot_li, state, f
               IF state.imfile EQ f THEN BEGIN
                  WIDGET_CONTROL, state.imtextid, SET_VALUE=f
               ENDIF
            ENDIF ELSE BEGIN
               MESSAGE, 'Error. Nothing before frame 0.' + bel, /INFO
            ENDELSE
         ENDIF
      ENDIF ELSE BEGIN
         MESSAGE, 'Error. No image.' + bel, /INFO
      ENDELSE
   END

   state.autoid : BEGIN
      IF state.imfile NE '' THEN BEGIN

         sameobject = 0
         fwhmok     = 0
         magok      = 0
         fail = 1
         fileok = 0
         firstpass=1
         thisframe = -1
         lastframe = -2

         REPEAT BEGIN

            WIDGET_CONTROL, state.cwitoolid, GET_VALUE=it_info
            lasttype = it_info.lasttype
            lastpos  = it_info.lastpos
            object   = it_info.object
            lastfwhm = it_info.lastfwhm
            lastmag  = it_info.lastmag

            IF it_info.lasttype NE 0 THEN BEGIN
               IF firstpass THEN BEGIN
                  ok=qannounc( GROUP=event.top, [ $
                        'This command will automatically proceed through the data with the same', $
                        'photometry extraction as done on the last frame.  By default, this process', $
                        'will continue until the object name changes, or, there is no more data.', $
                        'If this is what you wish to do, then select "Stop on Last".  However, you', $
                        'might want to stop on a certain frame number.  If you do want to stop on', $
                        'a certain frame number, then select "Set Stop Point".  If you choose the', $
                        'latter option, you will be asked for the desired stopping point.  However,', $
                        'a changing object name will take precedence over a stop point.  So, if you', $
                        'set a stop point after the object changes, it will stop earlier than you', $
                        'think.' $
                        ], XSIZE=80, YSIZE=10, TITLE='Automatic Photometry Procedure', $
                        TRUELABEL='Stop on Last', FALSELABEL='Set Stop Point' )
                  if ok then $
                     lastframe=999 $
                  else $
                     lastframe=qinput(prompt='Frame number to stop on?',/integer)
                  firstpass=0
               ENDIF
               f = state.imfile
               l = STRLEN( f )
               ext = STRMID( f, l-3, 3 )
               bext = BYTE( ext )
               t = WHERE( bext LT 48 AND bext GT 57, count )
               IF count EQ 0 THEN BEGIN
                  thisframe = FIX(ext)+1
                  next = STRING( thisframe, FORMAT='(I3.3)' )
                  STRPUT, f, next, l-3
                  ccdphot_li, state, f, AUTOPHOT=object, $
                     PHOTTYPE=lasttype, POSITION=lastpos
                  IF state.imfile EQ f THEN BEGIN
                     WIDGET_CONTROL, state.imtextid, SET_VALUE=f
                     WIDGET_CONTROL, state.cwitoolid, GET_VALUE=it_info
                     sameobject = (it_info.object EQ object)
                     magok      = (abs(it_info.lastmag-lastmag) LT 10.0)
                     fwhmok     = (abs(it_info.lastfwhm-lastfwhm)/lastfwhm LT 0.5 )
                     fail = 0
                     fileok = 1
                  ENDIF ELSE BEGIN
                     sameobject = 0
                     fileok = 0
                     magok = 0
                     fwhmok = 0
                  ENDELSE
               ENDIF ELSE BEGIN
                  thisframe=-1
                  MESSAGE,'  Filename cannot be incremented.  Unable to run Auto-photometry.'+bel,/INFO
               ENDELSE
            ENDIF ELSE BEGIN
               MESSAGE,'Cannot do auto-photometry.'+bel,/INFO
               MESSAGE,'   No position has yet been defined for this object.',/INFO
               lastframe=thisframe
            ENDELSE

         ENDREP UNTIL NOT sameobject OR lastframe EQ thisframe OR NOT magok OR NOT fwhmok

         IF NOT fail THEN BEGIN
         
            IF NOT fileok THEN $
               MESSAGE,'  Auto-photometry halted.  Next file incomplete or missing.' + bel, /INFO $

            ELSE IF NOT sameobject THEN $
               MESSAGE,'  Auto-photometry halted.  Object changed.' + bel, /INFO $

            ELSE IF thisframe GE lastframe THEN $
               MESSAGE,'  Auto-photometry halted.  Stopping point reached.' + bel, /INFO $

            ELSE IF NOT magok THEN $
               MESSAGE,'  Auto-photometry halted.  Magnitude of object changed too much.' + bel, /INFO $

            ELSE IF NOT fwhmok THEN $
               MESSAGE,'  Auto-photometry halted.  FWHM of object changed too much.' + bel, /INFO $

            ELSE $
               MESSAGE,'  Unknown ending condition for Auto, this should not happen.' + bel, /INFO

         ENDIF

      ENDIF ELSE BEGIN
         MESSAGE, 'Error. No image.' + bel, /INFO
      ENDELSE
   END

   state.vheaderid : BEGIN
      IF state.imfile NE '' THEN BEGIN
         WIDGET_CONTROL, event.id, GET_UVALUE=header
         t = 'Image Header' + ' - file ' + state.imfile
         viewtext, header, GROUP=event.top, TITLE=t, XSIZE=81, YSIZE=40
      ENDIF ELSE BEGIN
         MESSAGE, 'Error. No image.' + bel, /INFO
      ENDELSE
   END

   state.wfitsid : BEGIN
      ;Write the image to a FITS file( allows a calibrated image to be saved).
      IF state.imfile NE '' THEN BEGIN
         ;There is an image loaded. Do it.
         WIDGET_CONTROL, state.dimgptr, GET_UVALUE=image, /NO_COPY
         WIDGET_CONTROL, state.imparmsptr, GET_UVALUE=im_parms, /NO_COPY
         WIDGET_CONTROL, state.vheaderid, GET_UVALUE=header, /NO_COPY
         filename = 'c_' + im_parms.imfile
         writefits, filename, image, header
         MESSAGE, 'Image saved to file ' + filename, /INFO
         WIDGET_CONTROL, state.vheaderid, SET_UVALUE=header, /NO_COPY
         WIDGET_CONTROL, state.imparmsptr, SET_UVALUE=im_parms, /NO_COPY
         WIDGET_CONTROL, state.dimgptr, SET_UVALUE=image, /NO_COPY
      ENDIF
   END

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

WIDGET_CONTROL, stash, SET_UVALUE=state, /NO_COPY
END


; ------------------------------------------------------------------------------
; Procedure ccdphot
; Where it begins.
; ------------------------------------------------------------------------------
PRO ccdphot, CALIBFILE=in_calibfile, CALIBPATH=in_calibpath, $
             KEYLIST=in_keylist, $
             PATH=in_path, PHOTPARMFILE=in_photparmfile, $
             TMPLFILE=in_tmplfile, WZOOMFACT=wzoomfact

bel = STRING( 7B )

IF XREGISTERED( 'ccd' ) THEN RETURN

IF (!d.flags AND 256) EQ 0 THEN BEGIN
   MESSAGE, 'Error. No windowing device. CCDPHOT cannot be started.', /INFO
   RETURN
ENDIF

IF badpar(wzoomfact,[0,1,2,3],0, $
      CALLER='CCDPHOT: (wzoomfact) ',DEFAULT=0) THEN RETURN

IF badpar(in_path,[0,7],0, $
      CALLER='CCDPHOT: (path) ',DEFAULT='') THEN RETURN
if in_path ne '' then in_path=addslash(in_path)

IF badpar(in_calibpath,[0,7],0, $
      CALLER='CCDPHOT: (calibpath) ',DEFAULT=in_path+'calib/') THEN RETURN
in_calibpath=addslash(in_calibpath)

IF badpar(in_calibfile,[0,7],0, $
      CALLER='CCDPHOT: (calibfile) ',DEFAULT='files.cal') THEN RETURN
IF badpar(in_photparmfile,[0,7],0, $
      CALLER='CCDPHOT: (photparmfile) ',DEFAULT='') THEN RETURN
IF badpar(in_tmplfile,[0,7],0, $
      CALLER='CCDPHOT: (tmplfile) ',DEFAULT='') THEN RETURN

; Initialize the main state structure.
state = { $
   biasname:'', $
   biaspickid:0L, $
   biasptr:0L, $
   biastextid:0L, $
   calibfile:in_calibfile, calibflag:0, calibpath:in_calibpath, $
   calloadid:0L, $
   calpickid:0L, $
   calsaveid:0L, $
   caltextid:0L, $
   cimgptr:0L, $
   cwitoolbase:0L, cwitoolid:0L, $
   cxsize:0, cysize:0, $
   darkname:'', $
   darkpickid:0L, $
   darkptr:0L, $
   darktextid:0L, $
   dimgptr:0L, $
   editbase:0L, $
   dismissid:0L, $
   editid:0L, $
   exitid:0L, $
   flatbase:0L, $
   flatbase1:0L, $
   flathandid:0L, $
   flatindex:0, $
   flatlistid:0L, $
   flatpickid:0L, $
   flattextid:0L, $
   fselected:0, $
   hdrlistptr:0L, $
   imfile:'', $
   imparmsptr:0L, $
   impath:in_path, $
   impickid:0L, $
   imtextid:0L, $
   kill:0B, $
   mainbase:0L, $
   new:1B, $
   previd:0L, $
   nextid:0L, $
   nframes:0L, $
   okver:[ 'calib_file_v01', 'calib_file_v02', 'calib_file_v03' ], $
   overscan:'', $
   overscanid:0L, $
   photparmfile:in_photparmfile, $
   processflag:1, $
   autoid:0L, $
   pxsize:0, pysize:0, $
   rawproid:0L, $
   rimgptr:0L, $
   tmplfile:in_tmplfile, $
   vheaderid:0L, $
   wfitsid:0L, $
   wzoomfact:wzoomfact, $
   xl:-1, xr:-1, $
   x1:-1, x2:-1, y1:-1, y2:-1, $
   xrange:'*', xrangeid:0L, $
   yrange:'*', yrangeid:0L, $
   xsize:0, $
   ysize:0 }

; Get header correspondence list.
IF KEYWORD_SET( in_keylist ) THEN $
   loadkeys,in_keylist,hdrlist $
ELSE $
   loadkeys,'[[DEFAULT]]',hdrlist

;Define the main base.
mainbase = WIDGET_BASE( TITLE='CCDPHOT', /COLUMN, UVALUE=0 )
state.mainbase = mainbase
state.imparmsptr = mainbase

;Main button base.
b1 = WIDGET_BASE( mainbase, /ROW, /FRAME )

state.exitid = WIDGET_BUTTON( b1, VALUE='Exit' )
state.rawproid = cw_bgroup( b1, ['Raw', 'Processed'], /EXCLUSIVE, $
                 /NO_RELEASE, /ROW, SET_VALUE=state.processflag )
state.previd = WIDGET_BUTTON( b1, VALUE='Prev' )
state.nextid = WIDGET_BUTTON( b1, VALUE='Next' )
state.autoid = WIDGET_BUTTON( b1, VALUE='Auto' )
state.vheaderid = WIDGET_BUTTON( b1, VALUE='ViewHeader', UVALUE='' )
state.wfitsid = WIDGET_BUTTON( b1, VALUE='Write Fits' )

;Image select base.
imagebase = WIDGET_BASE( mainbase, /ROW, /FRAME, UVALUE=0 )
w1 = WIDGET_LABEL( imagebase, VALUE='IMAGE FRAME:', UVALUE=hdrlist )
state.impickid = WIDGET_BUTTON( imagebase, VALUE='Select File' )
state.imtextid = WIDGET_TEXT( imagebase, VALUE=state.imfile, /EDITABLE, $
                 XSIZE=40 )
;The header list is stored in the user-value of this base.
state.hdrlistptr = w1
state.rimgptr = imagebase
;

;Calibration environment file base.
calbase = WIDGET_BASE( mainbase, /COLUMN, /FRAME, UVALUE=0 )
w1 = WIDGET_LABEL( calbase, VALUE='CALIBRATION ENVIRONMENT' )
b2 = WIDGET_BASE( calbase, /ROW )
state.calloadid = WIDGET_BUTTON( b2, VALUE='Load' )
state.calsaveid = WIDGET_BUTTON( b2, VALUE='Save' )
state.editid    = WIDGET_BUTTON( b2, VALUE='Edit' )
state.calpickid = WIDGET_BUTTON( b2, VALUE='Select File' )
state.caltextid = WIDGET_TEXT( b2, VALUE=state.calibfile, /EDITABLE, XSIZE=40 )
;
state.cimgptr = calbase

; -------------------------------------------------
;The remaining widgets are based on a separate Top Level Base. This base
;is used to invoke a widget hierarchy which permits the calibration
;environment parameters to be edited. A separate event handler is associated
;with this TLB and follows my method for TLB event handlers (state retrieval
;information is stored in the user-value of the TLB).
state.editbase = WIDGET_BASE( TITLE='CCDPHOT Calibration Environment Editor', $
                 COLUMN=1 )

XMANAGER, '', state.editbase, EVENT_HANDLER='ccdphot_edit_eve', $
          GROUP_LEADER=mainbase, /JUST_REG

state.dismissid = WIDGET_BUTTON( state.editbase, VALUE='Dismiss' )

;Overscan region base.
overbase = WIDGET_BASE( state.editbase, ROW=1, FRAME=1 )
w1 = WIDGET_LABEL( overbase, VALUE='BIAS/OVERSCAN REGION:' )
state.overscanid = WIDGET_TEXT( overbase, VALUE=state.overscan, /EDITABLE, $
                   XSIZE=40 )

;Cropping region base.
cropbase = WIDGET_BASE( state.editbase, /ROW, /FRAME )
w1 = WIDGET_LABEL( cropbase, VALUE='CROPPING REGION:  x-range' )
state.xrangeid = WIDGET_TEXT(cropbase, VALUE=state.xrange, /EDITABLE, XSIZE=9 )
w1 = WIDGET_LABEL( cropbase, VALUE='     y-range' )
state.yrangeid = WIDGET_TEXT(cropbase, VALUE=state.yrange, /EDIT, XSIZE=9 )

;Biasframe base. The bias frame array will be stored in the user-value and
;is initialized to 0.0.
biasbase = WIDGET_BASE( state.editbase, /ROW, /FRAME, UVALUE=0 )
w1 = WIDGET_LABEL( biasbase, VALUE='BIAS FRAME:' )
state.biaspickid = WIDGET_BUTTON( biasbase, VALUE='Select File' )
state.biastextid = WIDGET_TEXT( biasbase, VALUE='', /EDIT, XSIZE=40 )
state.biasptr = biasbase

;Darkframe base. The dark frame array will be stored in the user-value and
;is initialized to 0.0.
darkbase = WIDGET_BASE( state.editbase, /ROW, /FRAME, UVALUE=0 )
w1 = WIDGET_LABEL( darkbase, VALUE='DARK FRAME:' )
state.darkpickid = WIDGET_BUTTON( darkbase, VALUE='Select File' )
state.darktextid = WIDGET_TEXT( darkbase, VALUE='', /EDIT, XSIZE=40 )
state.darkptr = darkbase

;Flat frames base.
flats = {filtercode:'None', filename:'', flatid:WIDGET_BASE(UVALUE=1.0, $
         GROUP_LEADER=mainbase)}
state.flatbase = WIDGET_BASE( state.editbase, /COLUMN, UVALUE=flats )
w1 = WIDGET_LABEL( state.flatbase, VALUE='LIST OF FLATS (filtercode filename)')

;Set-up the flats list widget.
state.flatlistid = WIDGET_LIST( state.flatbase, YSIZE=5 )

w1 = WIDGET_LABEL( state.flatbase, VALUE='Change Filename of Selected Flat:' )

b1 = WIDGET_BASE( state.flatbase, ROW=1 )
state.flatpickid = WIDGET_BUTTON( b1, VALUE='Select File' )
state.flattextid = WIDGET_TEXT( b1, VALUE='', /EDITABLE, XSIZE=40 )

WIDGET_CONTROL, state.flatpickid, SENSITIVE=0
WIDGET_CONTROL, state.flattextid, SENSITIVE=0

IF state.calibfile NE '' THEN BEGIN
   ;Call the calibration file load routine.
   ccdphot_lc, state
   ;Display the list of flats.
   WIDGET_CONTROL, state.flatbase, GET_UVALUE=flats, /NO_COPY
   WIDGET_CONTROL, state.flatlistid, SET_VALUE=ccdphot_fmt(flats)
   WIDGET_CONTROL, state.flatbase, SET_UVALUE=flats, /NO_COPY
ENDIF

;Stash area for the state structure.
stash = WIDGET_INFO( mainbase, /CHILD )

;Put the stash pointer into the edit TLB, so its event handler can access
;the main state.
WIDGET_CONTROL, state.editbase, SET_UVALUE={mainstash:stash}

;Stash the state structure.
WIDGET_CONTROL, stash, SET_UVALUE=state, /NO_COPY

;Realize the main base.
WIDGET_CONTROL, mainbase, /REALIZE

; Give control to the XMANAGER.
XMANAGER, 'ccdphot', mainbase, $
          EVENT_HANDLER='ccdphot_eve', $
          GROUP_LEADER=mainbase
END