Viewing contents of file '../idllib/contrib/buie/cw_itool.pro'
;+
; NAME:
;    cw_itool
; PURPOSE:
;    General purpose image display.
; DESCRIPTION:
; CATEGORY:
;    Compound Widgets
; CALLING SEQUENCE:
;    result = cw_itool( parent )
; INPUTS:
;    parent : The parent application base (Should be a Top Level Base).
; OPTIONAL INPUT PARAMETERS:
;    image : The image array to be displayed.
; KEYWORD PARAMETERS:
;  FVISIBLE       :  Size of the full-view window (128 pixels).
;  NODISMISS      :  If set, the Dismiss button is not sensitive.
;  PHOTPARMFILE   :  Optional photometry parameters file.
;  XSIZE          :  The image X-size (required).
;  YSIZE          :  The image Y-size (required).
;  WXVISIBLE      :  Creates work window with explicit x-size (500).
;  WYVISIBLE      :  Creates work window with explicit y-size (500).
;  WZOOMFACT      :  Ceiling for work-view zoom factor (unlimited).
;  ZVISIBLE       :  Size of the zoom window (128 pixels).
;
; OUTPUTS:
; OPTIONAL OUTPUT PARAMETERS:
;
; COMMON BLOCKS:
;
; SIDE EFFECTS:
;
; RESTRICTIONS:
;
; PROCEDURE:
;
; MODIFICATION HISTORY:
;    Written by Doug Loucks, Lowell Observatory, June 1994.
;    This version is a major re-write, employing the compound widget design
; introduced in Version 3 of IDL.
; 94/10/27, MWB, Lowell Observatory, fixed ZOON bug on line 1144.
; 95/01/24, MWB, xcen,ycen size mismatch storing back to template if array is
;           3-d.  Fixed to copy *last* position from Basphote return.
; 95/06/12, MWB, Fixed bug in stretch range computation for large cubes of
;           small images.
; 95/09/07, MWB, Changed auto-stretch computation to make it more robust.
; 95/10/31, MWB, Fixed widget layout problem caused by IDL v4.0 changes.
; 96/01/06, MWB, Changed W*VISIBLE default to recognize smaller screens.
; 96/01/16, MWB, Fixed !order=1 bug in mouse event handling.
;                Fixed Stretch Menu operational bugs for handling image cubes.
; 96/06/25, MWB, Added autophot support
; 97/12/12, MWB, changed display to include more image info on main window.
;-
; ------------------------------------------------------------------------------
; Internal Support Procedures:
;
; cw_itool_cstr   : Computes a stretch range for the image.
; cw_itool_disp   : Refreshes the image display label and text widgets.
; cw_itool_trk    : Refreshes the cursor tracking display widgets.
; cw_itool_draw   : Refreshes the draw widgets (full, zoom, and work).
; cw_itool_gvl    : Gets cw_itool's value. This value is defined to be a
;                   structure with a single tag of type string: {msg:['']}.
;                   If the string is non-null, cw_itool has determined that
;                   it should not be killed and has placed the reasons into
;                   the string tag as a string array.
; cw_itool_svl    : Sets cw_itool's value (defined as the im_parms structure).
; cw_itool_phact  : Action routine for template photometry.
; cw_itool_oneph  : Do one single object photometry operation.
; cw_draw_eve     :
; cw_itool_eve    : Master event handler for cw_itool.
; cw_pixed_exteve : Event handler for events arriving from the pixel editor.
; ------------------------------------------------------------------------------
; ------------------------------------------------------------------------------
; Procedure cw_itool_cstr
; Compute the stretch range for the image.
; ------------------------------------------------------------------------------
PRO cw_itool_cstr, im_parms, frame, SILENT=silent

IF im_parms.ready[frame] THEN RETURN

WIDGET_CONTROL, im_parms.imageptr, GET_UVALUE=image, /NO_COPY

minvalue = MIN( image[*,*,frame], MAX=maxvalue )

im_parms.minvalue[frame] = minvalue
im_parms.maxvalue[frame] = maxvalue

; Compute stretch range parameters.

; Grab 101 random pixels from image and sort them.
idx = randomu(seed,151) * ((im_parms.xsize*im_parms.ysize)-1) + $
        im_parms.xsize*im_parms.ysize*frame
timage = image[idx]
s = SORT( timage )
z_image = timage[ s ]

l = SIZE( s )
l = l[ 1 ]

medvalue = z_image[ l / 2 ]
t1 = 0.20 * l   ; old value was 0.02
t2 = 0.80 * l   ; old value was 0.90
sdvalue = STDEV( z_image[ t1 : t2 ] )
m1 = z_image[ t1 ]
m2 = z_image[ t2 ]
timage = 0
z_image = 0

dm = 3
dp = 5
f = '(G0.0)'

z1 = medvalue - sdvalue * dm
z2 = medvalue + sdvalue * dp

IF z1 EQ z2 THEN BEGIN
   z1 = minvalue
   z2 = maxvalue
ENDIF

IF z1 EQ z2 THEN $
   z2 = z2 + 1

im_parms.sclmin[frame] = z1
im_parms.sclmax[frame] = z2

IF im_parms.sclmin[frame] LT im_parms.minvalue[frame] THEN BEGIN
   im_parms.sclmin[frame] = im_parms.minvalue[frame]
ENDIF

IF im_parms.sclmax[frame] GT im_parms.maxvalue[frame] THEN BEGIN
   im_parms.sclmax[frame] = im_parms.maxvalue[frame]
ENDIF

im_parms.curmin[frame] = im_parms.sclmin[frame]
im_parms.curmax[frame] = im_parms.sclmax[frame]

im_parms.ready[frame] = 1B

IF NOT KEYWORD_SET( silent ) THEN BEGIN
;  PRINT, ''
   PRINT, 'Median: ' + STRING( medvalue, FORMAT='(G0.0)' ) + $
   '  Sigma: ' + STRING( sdvalue, FORMAT='(G0.0)' ) + $
   '  Sub-array min, max: ' + STRING( m1, FORMAT='(G0.0)' ) + ', ' + $
   STRING( m2, FORMAT='(G0.0)' )
;  PRINT, ''

;  MESSAGE, 'Statistics for image ' + im_parms.imfile + ':', /INFO
;  PRINT, prefix + 'median ' + STRING(medvalue) + '  sigma ' + STRING(sdvalue)
;  PRINT, prefix + 'stretch range ' + STRING( z1, FORMAT=f ) + ' to ' + $
;  STRING( z2, FORMAT=f ) + ' (-' + dms + ' sigma to +' + $
;  dps + ' sigma)'
;  PRINT, prefix + 'sub-array min and max: ' + STRING(m1) + STRING(m2)
ENDIF


WIDGET_CONTROL, im_parms.imageptr, SET_UVALUE=image, /NO_COPY
END


; ------------------------------------------------------------------------------
; Procedure cw_itool_disp
; Update the display widgets.
; ------------------------------------------------------------------------------
PRO cw_itool_disp, state

WIDGET_CONTROL, state.mainbase, UPDATE=0

WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
frame = im_parms.frame

;Compute stretch, if necessary.
cw_itool_cstr, im_parms, frame

maxvalue = STRING( im_parms.maxvalue[frame], FORMAT=state.fmtg )
minvalue = STRING( im_parms.minvalue[frame], FORMAT=state.fmtg )

; Show the image min and max.
WIDGET_CONTROL, state.imagemaxid, SET_VALUE=maxvalue
WIDGET_CONTROL, state.imageminid, SET_VALUE=minvalue

; Show the frame number.
WIDGET_CONTROL, state.frameid, SET_VALUE=STRING( im_parms.frame, $
       FORMAT=state.fmti )

;Show the total number of frames in this image file.
WIDGET_CONTROL, state.nframesid, SET_VALUE=STRING( im_parms.nframes, $
       FORMAT=state.fmti )

IF im_parms.imfile NE '' THEN BEGIN
   ;Show the image file name.
   title = im_parms.title + ' ' + im_parms.imfile
   WIDGET_CONTROL, state.mainbase, TLB_SET_TITLE=title
ENDIF

;Show the object name.
IF im_parms.object ne '' or im_parms.filter ne '' or im_parms.ut ne '' THEN BEGIN
   str1=strmid('Object: '+im_parms.object,0,20)
   str2=strmid('Filter: '+im_parms.filter,0,20)
   str3=string('Airmas: ',im_parms.airmass,format='(a,f5.3)')
   str4=strcompress(string('ExpTim: ',im_parms.exptime,format='(a,f10.3," sec")'))
   jdstr,im_parms.jd,0,jds
   str5='    UT: '+strmid(jds,0,10)
   str6='midtim: '+strmid(jds,11,8)
ENDIF ELSE BEGIN
   str1=''
   str2=''
   str3=''
   str4=''
   str5=''
   str6=''
ENDELSE
WIDGET_CONTROL, state.objectid, SET_VALUE=str1
WIDGET_CONTROL, state.filterid, SET_VALUE=str2
WIDGET_CONTROL, state.airmasid, SET_VALUE=str3
WIDGET_CONTROL, state.exptimid, SET_VALUE=str4
WIDGET_CONTROL, state.utdateid, SET_VALUE=str5
WIDGET_CONTROL, state.uttimeid, SET_VALUE=str6

;Set some button sensitivities.
WIDGET_CONTROL, state.auto2id,     SENSITIVE=(im_parms.nframes GT 1)
WIDGET_CONTROL, state.disp2id,     SENSITIVE=(im_parms.nframes GT 1)
WIDGET_CONTROL, state.extrema2id,  SENSITIVE=(im_parms.nframes GT 1)
WIDGET_CONTROL, state.animateid,   SENSITIVE=(im_parms.nframes GT 1)
WIDGET_CONTROL, state.frameid,     SENSITIVE=(im_parms.nframes GT 1)
WIDGET_CONTROL, state.nextframeid, SENSITIVE=(im_parms.nframes GT 1)

curmin = STRING( im_parms.curmin[frame], FORMAT=state.fmtg )
curmax = STRING( im_parms.curmax[frame], FORMAT=state.fmtg )

;Close access to the image parameters structure.
WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY

; Show the stretch range for the display.
WIDGET_CONTROL, state.dispminid, SET_VALUE=curmin
WIDGET_CONTROL, state.dispmaxid, SET_VALUE=curmax

;Update the image parameters widget.
child = WIDGET_INFO( state.imparmsbase, /CHILD )
IF child NE 0L THEN BEGIN
   ;Refresh the image parameters widget.
   WIDGET_CONTROL, child, SET_VALUE=0
ENDIF

WIDGET_CONTROL, state.mainbase, UPDATE=1
END


; ------------------------------------------------------------------------------
; Procedure cw_itool_trk
; Update the cursor tracking widgets.
; ------------------------------------------------------------------------------
PRO cw_itool_trk, draw_state, in_x, in_y

x = fix(in_x+0.5)
y = fix(in_y+0.5)

WIDGET_CONTROL, draw_state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
WIDGET_CONTROL, im_parms.imageptr, GET_UVALUE=image, /NO_COPY
count = image[ x, y, im_parms.frame ]
WIDGET_CONTROL, im_parms.imageptr, SET_UVALUE=image, /NO_COPY
WIDGET_CONTROL, draw_state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY

curpos = STRING( 'xloc= ', x, '   yloc= ', y, $
'   count= ', count, FORMAT='(A,I4,A,I4,A,G0.0)' )
WIDGET_CONTROL, draw_state.curposid, UPDATE=0
WIDGET_CONTROL, draw_state.curposid, SET_VALUE=curpos
WIDGET_CONTROL, draw_state.curposid, UPDATE=1

END


; ------------------------------------------------------------------------------
; Procedure cw_itool_draw
; Update one or more of the draw widgets.
; ------------------------------------------------------------------------------
;
PRO cw_itool_draw, draw_state, FULL=full, WORK=work, ZOOM=zoom, $
    ZXCEN=zxcen, ZYCEN=zycen

;Optional zoom center update is provided via the keywords.

;Open access to data.
WIDGET_CONTROL, draw_state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
WIDGET_CONTROL, im_parms.imageptr, GET_UVALUE=image, /NO_COPY

frame  = im_parms.frame
curmin = im_parms.curmin[frame]
curmax = im_parms.curmax[frame]

curwin = !d.window

IF KEYWORD_SET( full ) THEN BEGIN
   ;Process the full window.
   WIDGET_CONTROL, draw_state.full_view, GET_UVALUE=fullstate, /NO_COPY
   WIDGET_CONTROL, fullstate.id, GET_VALUE=winnum
   WSET, winnum

   IF fullstate.zfact NE 1 THEN BEGIN
      TV, BYTSCL( REBIN( image[fullstate.spx : fullstate.spx + fullstate.dx-1, $
                               fullstate.spy : fullstate.spy + fullstate.dy-1, $
                  frame], $
                  fullstate.dx*fullstate.zfact, fullstate.dy*fullstate.zfact, $
                  /SAMPLE ), $
          MIN=curmin, MAX=curmax, TOP=!D.N_COLORS-1 ), $
      fullstate.xoff, fullstate.yoff
   ENDIF ELSE BEGIN
      TV, BYTSCL( image[fullstate.spx : fullstate.spx+fullstate.dx-1, $
                        fullstate.spy : fullstate.spy+fullstate.dy-1, frame], $
          MIN=curmin, MAX=curmax, TOP=!D.N_COLORS-1 ), $
      fullstate.xoff, fullstate.yoff
   ENDELSE
   WIDGET_CONTROL, draw_state.full_view, SET_UVALUE=fullstate, /NO_COPY
ENDIF

IF KEYWORD_SET( zoom ) THEN BEGIN
   ;Process the zoom window.
   WIDGET_CONTROL, draw_state.zoom_view, GET_UVALUE=zoomstate, /NO_COPY
   WIDGET_CONTROL, zoomstate.id, GET_VALUE=winnum
   WSET, winnum

   IF KEYWORD_SET( zxcen ) THEN zoomstate.xcen = fix(zxcen+0.5)
   IF KEYWORD_SET( zycen ) THEN zoomstate.ycen = fix(zycen+0.5)

   ;Compute zoom window parameters.
   zoomstate.dx = zoomstate.xvisible / zoomstate.zfact
   IF zoomstate.dx GT im_parms.xsize THEN zoomstate.dx=im_parms.xsize
   IF zoomstate.dx GT im_parms.ysize THEN zoomstate.dx=im_parms.ysize

   zoomstate.dy = zoomstate.dx

   zoomstate.spx = zoomstate.xcen - ( zoomstate.dx / 2 )
   zoomstate.spy = zoomstate.ycen - ( zoomstate.dy / 2 )

   IF (zoomstate.spx + zoomstate.dx) GE im_parms.xsize THEN BEGIN
      zoomstate.spx = im_parms.xsize - zoomstate.dx - 1
      zoomstate.xcen = zoomstate.spx + ( zoomstate.dx / 2 )
   ENDIF

   IF (zoomstate.spy + zoomstate.dy) GE im_parms.ysize THEN BEGIN
      zoomstate.spy = im_parms.ysize - zoomstate.dy - 1
      zoomstate.ycen = zoomstate.spy + ( zoomstate.dy / 2 )
   ENDIF

   IF zoomstate.spx LT 0 THEN zoomstate.spx = 0
   IF zoomstate.spy LT 0 THEN zoomstate.spy = 0

   zoomstate.xsize = zoomstate.dx * zoomstate.zfact
   zoomstate.ysize = zoomstate.dy * zoomstate.zfact

   zoomstate.xoff = ( zoomstate.xvisible - zoomstate.xsize ) / 2
   zoomstate.yoff = ( zoomstate.yvisible - zoomstate.ysize ) / 2

   IF zoomstate.zfact NE 1 THEN BEGIN
      TV, BYTSCL( REBIN( image[zoomstate.spx : zoomstate.spx + zoomstate.dx-1, $
                               zoomstate.spy : zoomstate.spy + zoomstate.dy-1, $
                  frame], $
                  zoomstate.zfact*zoomstate.dx, zoomstate.zfact*zoomstate.dy, $
                  /SAMPLE ), $
          MIN=curmin, MAX=curmax, TOP=!D.N_COLORS-1 ), $
      zoomstate.xoff, zoomstate.yoff
   ENDIF ELSE BEGIN
      TV, BYTSCL( image[zoomstate.spx : zoomstate.spx+zoomstate.dx-1, $
                        zoomstate.spy : zoomstate.spy+zoomstate.dy-1, frame], $
          MIN=curmin, MAX=curmax, TOP=!D.N_COLORS-1 ), $
      zoomstate.xoff, zoomstate.yoff
   ENDELSE
   WIDGET_CONTROL, draw_state.zoom_view, SET_UVALUE=zoomstate, /NO_COPY
ENDIF

IF KEYWORD_SET( work ) THEN BEGIN
   WIDGET_CONTROL, draw_state.work_view, GET_UVALUE=workstate, /NO_COPY
   WIDGET_CONTROL, workstate.id, GET_VALUE=winnum
   WSET, winnum

   IF workstate.zfact NE 1 THEN BEGIN
      TV, BYTSCL( REBIN( image[workstate.spx : workstate.spx + workstate.dx-1, $
                               workstate.spy : workstate.spy + workstate.dy-1, $
                  frame], $
                  workstate.zfact*workstate.dx, workstate.zfact*workstate.dy, $
                  /SAMPLE ), $
          MIN=curmin, MAX=curmax, TOP=!D.N_COLORS-1 ), $
      workstate.xoff, workstate.yoff
   ENDIF ELSE BEGIN
      TV, BYTSCL( image[workstate.spx : workstate.spx+workstate.dx-1, $
                        workstate.spy : workstate.spy+workstate.dy-1, frame], $
          MIN=curmin, MAX=curmax, TOP=!D.N_COLORS-1 ), $
      workstate.xoff, workstate.yoff
   ENDELSE
   WIDGET_CONTROL, draw_state.work_view, SET_UVALUE=workstate, /NO_COPY
ENDIF

;Restore the previous window number.
WSET, curwin

;Refresh any profiles widgets that may be active.
;Open access to the profiles instances array.
WIDGET_CONTROL, draw_state.pfilesid, GET_UVALUE=instances, /NO_COPY
v = WIDGET_INFO( instances, /VALID_ID )
w = WHERE( v NE 0, count )
IF count NE 0 THEN BEGIN
   ;There is at least one profiles widget active.
   frame = im_parms.frame

   WIDGET_CONTROL, draw_state.zoom_view, GET_UVALUE=zoomstate, /NO_COPY
   spx = zoomstate.spx
   spy = zoomstate.spy
   dx  = zoomstate.dx
   dy  = zoomstate.dy
   WIDGET_CONTROL, draw_state.zoom_view, SET_UVALUE=zoomstate, /NO_COPY

   array = image[spx:spx+dx-1, spy:spy+dy-1, frame]

   FOR j=0, count-1 DO BEGIN
      child = WIDGET_INFO( instances[w[j]], /CHILD )
      IF child NE 0L THEN BEGIN
         ;Found an active profile. Request an update by using the set_value
         ;keyword to widget_control (The profiles widget will ignore the
         ;update request if the display is locked).
         WIDGET_CONTROL, child, SET_VALUE={image:array, xset:spx, yset:spy}
      ENDIF
   ENDFOR
ENDIF

;Close access to the profiles instances array.
WIDGET_CONTROL, draw_state.pfilesid, SET_UVALUE=instances, /NO_COPY

;Close access to data.
WIDGET_CONTROL, im_parms.imageptr, SET_UVALUE=image, /NO_COPY
WIDGET_CONTROL, draw_state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY
END


; ------------------------------------------------------------------------------
; Function cw_itool_gvl
; ------------------------------------------------------------------------------
FUNCTION cw_itool_gvl, id

stash = WIDGET_INFO( id, /CHILD )
WIDGET_CONTROL, stash, GET_UVALUE=state, /NO_COPY

WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
lasttype = im_parms.lasttype
lastpos  = im_parms.lastpos
object   = im_parms.object
lastmag  = im_parms.lastmag
lastfwhm = im_parms.lastfwhm
WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY


WIDGET_CONTROL, state.phparmsbase, GET_UVALUE=ph_parms, /NO_COPY
phedit = ph_parms.edtflg
WIDGET_CONTROL, state.phparmsbase, SET_UVALUE=ph_parms, /NO_COPY

tpmgrid = WIDGET_INFO( state.tpmgrbase, /CHILD )

IF tpmgrid NE 0L THEN BEGIN
   WIDGET_CONTROL, tpmgrid, GET_VALUE=tp_list
   nplates = N_ELEMENTS( tp_list )
ENDIF ELSE BEGIN
   nplates = 0
ENDELSE

modified = 0B
IF nplates GT 0 THEN BEGIN
   FOR j=0, nplates-1 DO BEGIN
      IF tp_list[j].tlbid NE 0L THEN BEGIN
         WIDGET_CONTROL, tp_list[j].tpid, GET_VALUE=tplate
         modified = modified OR tplate.modified
      ENDIF
   ENDFOR
ENDIF

msg = ['']

IF phedit THEN BEGIN
   msg = [ msg, $
   'Itool PHOTOMETRY parameter changes have not been saved.' ]
ENDIF

IF modified THEN BEGIN
   msg = [ msg, $
   'Itool TEMPLATE changes have not been saved.' ]
ENDIF

flag = phedit OR modified

WIDGET_CONTROL, stash, SET_UVALUE=state, /NO_COPY

RETURN, {flag:flag, msg:msg, lasttype:lasttype, lastpos:lastpos, $
         object:object, lastfwhm:lastfwhm, lastmag:lastmag}
END


; ------------------------------------------------------------------------------
; Procedure cw_itool_svl
; The 'set value' procedure for cw_itool.
; ------------------------------------------------------------------------------
PRO cw_itool_svl, id, im_parms

stash = WIDGET_INFO( id, /CHILD )
WIDGET_CONTROL, stash, GET_UVALUE=state, /NO_COPY

cw_itool_cstr, im_parms, 0

IF NOT im_parms.autophot THEN im_parms.lasttype=0

;Put the new image parameters into the holding area.
WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms

cw_itool_disp, state

cw_itool_draw, state, /FULL, /ZOOM, /WORK

WIDGET_CONTROL, state.phparmsbase, GET_UVALUE=ph_parms, /NO_COPY
ph_parms.objnum = 0
WIDGET_CONTROL, state.phparmsbase, SET_UVALUE=ph_parms, /NO_COPY

IF state.scroll THEN BEGIN
   WIDGET_CONTROL, state.work_view, GET_UVALUE=workstate, /NO_COPY
   xoff = MAX( [0, (im_parms.xsize-workstate.xvisible)/2 ] )
   yoff = MAX( [0, (im_parms.ysize-workstate.yvisible)/2 ] )
   WIDGET_CONTROL, state.work_view, SET_DRAW_VIEW=[xoff, yoff]
   WIDGET_CONTROL, state.work_view, SET_UVALUE=workstate, /NO_COPY
ENDIF

IF im_parms.autophot THEN BEGIN
   WIDGET_CONTROL, state.drawbase, GET_UVALUE=draw_state, /NO_COPY
   IF im_parms.lasttype EQ 1 THEN BEGIN
      cw_itool_oneph, draw_state, im_parms.lastpos[0], im_parms.lastpos[1]
   ENDIF ELSE IF im_parms.lasttype EQ 2 THEN BEGIN
      cw_itool_phact, draw_state, im_parms.lastpos[0], im_parms.lastpos[1]
   ENDIF
   WIDGET_CONTROL, state.drawbase, SET_UVALUE=draw_state, /NO_COPY
ENDIF

WIDGET_CONTROL, stash, SET_UVALUE=state, /NO_COPY
END


; ------------------------------------------------------------------------------
; Procedure cw_itool_phact
; Action routine for Template Photometry.
; ------------------------------------------------------------------------------
PRO cw_itool_phact, draw_state, x, y

bel = STRING(7B)

;Get id of the Template Manager.
tpmgrid = WIDGET_INFO( draw_state.tpmgrbase, /CHILD )

;Retrieve the 'value' of the template manager, which is the template list
;array.
IF tpmgrid NE 0L THEN BEGIN
   WIDGET_CONTROL, tpmgrid, GET_VALUE=tp_list
   selected = WHERE( tp_list[*].selected EQ 1B, count )
ENDIF ELSE BEGIN
   count = 0
ENDELSE

IF count EQ 1 THEN BEGIN
   ;Template is selected.
   WIDGET_CONTROL, tp_list[selected].tpid, GET_VALUE=tplate
ENDIF ELSE BEGIN
   ;Template not selected.
   MESSAGE, 'Error. Template must be selected.'+bel, /INFO
   RETURN
ENDELSE

CASE tplate.mode OF
   0 : BEGIN ;Add mode.
      IF tplate.numobj EQ 0 THEN BEGIN
         ;This is the first (anchor) position.
         tplate.new[0] = 1B
         tplate.x[0]  = x
         tplate.y[0]  = y
         tplate.numobj = 1
      ENDIF ELSE BEGIN
         ;This is a position to be added.
         numobj = tplate.numobj + 1
         mode   = tplate.mode
         modified = tplate.modified
         new = [tplate.new, 1B]
         objnam = [tplate.objnam, '<default>' ]
         nx = [tplate.x, x]
         ny = [tplate.y, y]
         tplate = {numobj:numobj, mode:mode, modified:modified, new:new, $
                   objnam:objnam, x:nx, y:ny}
      ENDELSE

      tplate.modified = 1B
      WIDGET_CONTROL, tp_list[selected].tpid, SET_VALUE=tplate
      cw_itool_trk, draw_state, x, y
      WIDGET_CONTROL,draw_state.imparmsbase,GET_UVALUE=im_parms,/NO_COPY
      im_parms.lasttype=0
      WIDGET_CONTROL,draw_state.imparmsbase,SET_UVALUE=im_parms,/NO_COPY
   END

   1 : BEGIN ;Left-button photometry mode.
      IF tplate.numobj EQ 0 THEN BEGIN
         MESSAGE, 'Error. No objects in this template.'+STRING(7B), /INFO
         RETURN
      ENDIF

      ;The values of x and y are assumed to be the new anchor coordinates.

      IF tplate.new[0] EQ 1B THEN BEGIN
         xguess = tplate.x
         yguess = tplate.y
      ENDIF ELSE BEGIN
         dx = x - tplate.x[0]
         dy = y - tplate.y[0]
         xguess = tplate.x
         yguess = tplate.y
         w = WHERE( tplate.new EQ 0B, count )
         IF count GT 0 THEN BEGIN
            xguess[w] = xguess[w] + dx[w]
            yguess[w] = yguess[w] + dy[w]
         ENDIF
      ENDELSE

      ;Offset of mouse position from last known position.
      axoff = xguess[0]-tplate.x[0]
      ayoff = yguess[0]-tplate.y[0]

      ;Check if first position is within tolerance of the anchor.
      IF (ABS(axoff) GT 20) OR (ABS(ayoff) GT 20) THEN BEGIN
         t = [ $
         '  The location of the first object, as supplied by the left mouse', $
         'button, is more than twenty pixels away from its last known', $
         'location.', $
         '  The template could become corrupted if this starting location', $
         'is used.', $
         '  The requested starting location, last known location, and', $
         'differences are shown below:', $
         '                    ', $
         '------ Requested (x,y) ----- Last known (x,y) ---- differences', $
         '  (' + STRING( x, FORMAT='(F10.3)' ) + ',' + $
                 STRING( y, FORMAT='(F8.3)' ) + ')' + $
         '  (' + STRING( tplate.x[0],   FORMAT='(F10.3)' ) + ',' + $
                 STRING( tplate.y[0],   FORMAT='(F8.3)') + ')' + $
         '  (' + STRING( x-tplate.x[0],   FORMAT='(F10.3)' ) + ',' + $
                 STRING( y-tplate.y[0],   FORMAT='(F8.3)' ) + ')' ]

         con = qannounc( t, TITLE='First Object Location Status', $
               FALSE='Cancel this request', TRUE='Ok, start photometry', $
               XSIZE=80, YSIZE=12 )
         IF NOT con THEN RETURN
      ENDIF

      tpn = tplate.objnam

      WIDGET_CONTROL, draw_state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
      WIDGET_CONTROL, draw_state.phparmsbase, GET_UVALUE=ph_parms, /NO_COPY
      WIDGET_CONTROL, im_parms.imageptr, GET_UVALUE=image, /NO_COPY

      ;Check for header substitution of object name.
      w = WHERE( STRUPCASE(tpn) EQ '<DEFAULT>', count )
      IF count NE 0 THEN tpn[w]=im_parms.object

      objnum = ph_parms.objnum
      basphote, ph_parms.gain, image, $
         im_parms.exptime, xguess, yguess, ph_parms.radius, $
         ph_parms.sky1, ph_parms.sky2, ph_parms.logfile, objnum, $
         AIRMASS=im_parms.airmass, /ALTLOG, BOXMRAD=ph_parms.boxmrad, $
         EXACT=ph_parms.exact, FWHM=fwhm, MAG=mag, NAME=tpn, $
         NOMEXT=ph_parms.nomext, FILTER=im_parms.filter, $
         FNAME=im_parms.imfile, JD=im_parms.jd, DT=im_parms.expdelta, $
         ONCHIP=onchip, PSCALE=ph_parms.pscale, $
         XCEN=xcen, YCEN=ycen, ZPOINT=ph_parms.zpoint
      ph_parms.objnum = objnum
      im_parms.lasttype=2
      im_parms.lastpos=[fix(xcen[0]+0.5),fix(ycen[0]+0.5)]
      im_parms.lastmag=mag[0]
      im_parms.lastfwhm=fwhm[0]

      WIDGET_CONTROL, im_parms.imageptr, SET_UVALUE=image, /NO_COPY
      WIDGET_CONTROL, draw_state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY
      WIDGET_CONTROL, draw_state.phparmsbase, SET_UVALUE=ph_parms, /NO_COPY

      ;Re-draw the zoom window at the new center.
      cw_itool_draw, draw_state, /ZOOM, ZXCEN=xcen[0], ZYCEN=ycen[0]
      cw_itool_trk, draw_state, xcen[0], ycen[0]

      ;Locate the objects found by Basphote that are not new.
      found = WHERE( (onchip EQ 1B) AND (tplate.new EQ 0B), count )

      ;Locate the objects not found (off chip).
      lost = WHERE( onchip EQ 0B, lostcount )

      IF count GT 3 THEN BEGIN
         ;Compute the difference vectors to be used for the stats..
         dx = xcen[found] - xguess[found]
         dy = ycen[found] - yguess[found]
         sigmadx = STDEV( dx[1 : count-1], meandx )
         sigmady = STDEV( dy[1 : count-1], meandy )
      ENDIF ELSE BEGIN
         ;Too few positions to obtain stats.
         meandx  = 0
         meandy  = 0
         sigmadx = 0
         sigmady = 0
      ENDELSE

      PRINT, 'Template sigmas: ', sigmadx, sigmady

      IF lostcount GT 0 THEN BEGIN
         ;Fix the positions of the lost object(s} by adjusting the last known
         ;position(s).
         xcen[lost] = xguess[lost] + meandx
         ycen[lost] = yguess[lost] + meandy
      ENDIF

      ;Old position.
      oldx = tplate.x[1:tplate.numobj-1]
      oldy = tplate.y[1:tplate.numobj-1]

      ;New position.
      newx = xcen[1:tplate.numobj-1]
      newy = ycen[1:tplate.numobj-1]

      ;Differencees.
      dx = newx - oldx
      dy = newy - oldy

      ;Make a sequence string vector.
      seqnum = STRING( INDGEN(tplate.numobj), FORMAT='(I2)' )
      seqnum = seqnum[ 1:tplate.numobj-1 ]

      newpos = WHERE( tplate.new EQ 1B, newcount )

      ;Make a flags string vector.
      flags = REPLICATE( ' ', tplate.numobj )
      IF lostcount GT 0 THEN flags[lost]='*'
      IF newcount  GT 0 THEN flags[newpos]='+'
      flags = flags[ 1:tplate.numobj-1 ]

      ; Define a threshold value for the computed sigmas.
      sigmathresh = 0.5

      ; Test for sigmas greater than this threshold.
      IF sigmadx GT sigmathresh OR sigmady GT sigmathresh THEN BEGIN
         t = [ $
         '  The positions of objects in the active template have changed,', $
         'relative to each other, by an amount which may be too large.', $
         'Template corruption is possible.', $
         '  Please review the following table of positions and decide', $
         'whether to accept or ignore the new positions.', $
         '  The table lists the last known locations, the new locations,', $
         'and their differences.', $
         '        ', $
         '   ------ Last known (x,y) ----- New (x,y) --------- differences', $
         '     (' + STRING( tplate.x[0], FORMAT='(F10.3)' ) + ',' + $
                 STRING( tplate.y[0], FORMAT='(F8.3)'  ) + ')' + $
         '  (' + STRING( xcen[0], FORMAT='(F10.3)' ) + ',' + $
                 STRING( ycen[0], FORMAT='(F8.3)'  ) + ')' + $
         '  (' + STRING( xcen[0]-tplate.x[0], FORMAT='(F10.3)' ) + ',' + $
                 STRING( ycen[0]-tplate.y[0], FORMAT='(F8.3)'  ) + $
                         ')  (anchor)', $
         '        ', $
         seqnum + ' ' + $
         '  (' + STRING( oldx, FORMAT='(F10.3)' ) + ',' + $
                 STRING( oldy, FORMAT='(F8.3)'  ) + ')' + $
         '  (' + STRING( newx, FORMAT='(F10.3)' ) + ',' + $
                 STRING( newy, FORMAT='(F8.3)'  ) + ')' + $
         '  (' + STRING( dx, FORMAT='(F10.3)' ) + ',' + $
                 STRING( dy, FORMAT='(F8.3)'  ) + ')     ' + flags, $
         ' ', $
         '           Legend: * offchip, + new object' ]

         con = qannounc( t, TITLE='Template Object Verification', $
               FALSE='Ignore, avoid template corruption', $
               TRUE='Accept new object locations', $
               XSIZE=90, ysize=16 )
         IF NOT con THEN BEGIN
            WIDGET_CONTROL,draw_state.imparmsbase,GET_UVALUE=im_parms,/NO_COPY
            im_parms.lasttype=0
            WIDGET_CONTROL,draw_state.imparmsbase,SET_UVALUE=im_parms,/NO_COPY
            RETURN
         ENDIF

      ENDIF

      ;Update the template to the new positions.
      csz = size(xcen)
      if csz[0] ne 2 then begin
         tplate.x = xcen
         tplate.y = ycen
      endif else begin
         tplate.x = xcen[*,csz[2]-1]
         tplate.y = ycen[*,csz[2]-1]
      endelse
      tplate.modified = 1B
      tplate.new = 0B
      WIDGET_CONTROL, tp_list[selected].tpid, SET_VALUE=tplate
   END
ENDCASE

END

; ------------------------------------------------------------------------------
; Procedure cw_itool_oneph
; Do single object photometry with display update.
; ------------------------------------------------------------------------------
PRO cw_itool_oneph, draw_state, x, y

   WIDGET_CONTROL, draw_state.imparmsbase, GET_UVALUE=im_parms, $
      /NO_COPY
   WIDGET_CONTROL, draw_state.phparmsbase, GET_UVALUE=ph_parms, $
      /NO_COPY
   WIDGET_CONTROL, im_parms.imageptr, GET_UVALUE=image, /NO_COPY

   objnum = ph_parms.objnum
   basphote, ph_parms.gain, image, $
      im_parms.exptime, x, y, ph_parms.radius, $
      ph_parms.sky1, ph_parms.sky2, ph_parms.logfile, objnum, $
      AIRMASS=im_parms.airmass, /ALTLOG, BOXMRAD=ph_parms.boxmrad, $
      EXACT=ph_parms.exact, FWHM=fwhm, MAG=mag, NAME=im_parms.object, $
      NOMEXT=ph_parms.nomext, FILTER=im_parms.filter, $
      FNAME=im_parms.imfile, JD=im_parms.jd, DT=im_parms.expdelta, $
      PSCALE=ph_parms.pscale, $
      XCEN=xcen, YCEN=ycen, ZPOINT=ph_parms.zpoint
   ph_parms.objnum = objnum

   im_parms.lasttype=1
   im_parms.lastpos=[fix(xcen[0]+0.5),fix(ycen[0]+0.5)]
   im_parms.lastmag=mag[0]
   im_parms.lastfwhm=fwhm[0]

   WIDGET_CONTROL, im_parms.imageptr, SET_UVALUE=image, /NO_COPY
   WIDGET_CONTROL, draw_state.imparmsbase, SET_UVALUE=im_parms, $
      /NO_COPY
   WIDGET_CONTROL, draw_state.phparmsbase, SET_UVALUE=ph_parms, $
      /NO_COPY

   ;Re-draw the zoom window at the new center.
   cw_itool_draw, draw_state, /ZOOM, ZXCEN=xcen[0], ZYCEN=ycen[0]

   ;Update the cursor tracking widgets.
   cw_itool_trk, draw_state, xcen[0], ycen[0]

END

; ------------------------------------------------------------------------------
; Function cw_draw_eve
; Draw event handler.
; ------------------------------------------------------------------------------
FUNCTION cw_draw_eve, event
WIDGET_CONTROL, event.id, /HOURGLASS
WIDGET_CONTROL, event.handler, GET_UVALUE=draw_state, /NO_COPY
bel = STRING(7B)

out_event = 0

CASE event.id OF
   draw_state.full_view : BEGIN
      WIDGET_CONTROL, event.id, GET_UVALUE=fullstate, /NO_COPY

      IF fullstate.zfact NE 1.0 THEN BEGIN
         x = fullstate.spx + ( event.x - fullstate.xoff ) / fullstate.zfact
         y = fullstate.spy + ( event.y - fullstate.yoff ) / fullstate.zfact
      ENDIF ELSE BEGIN
         x = fullstate.spx + ( event.x - fullstate.xoff )
         y = fullstate.spy + ( event.y - fullstate.yoff )
      ENDELSE

      IF !order NE 0 THEN BEGIN
         y = fullstate.ysize - 1 - y
      ENDIF

      CASE event.press OF
         2 : BEGIN
            IF (draw_state.scroll) THEN BEGIN
               WIDGET_CONTROL, draw_state.work_view, GET_UVALUE=workstate, $
                  /NO_COPY
               xoff = x - workstate.xvisible / 2
               yoff = y - workstate.yvisible / 2
               IF !order NE 0 THEN BEGIN
                  WIDGET_CONTROL, draw_state.imparmsbase, GET_UVALUE=im_parms, $
                                  /NO_COPY
                  yoff = im_parms.ysize - ( y + workstate.yvisible / 2 )
                  WIDGET_CONTROL, draw_state.imparmsbase, SET_UVALUE=im_parms, $
                                  /NO_COPY
               ENDIF
               IF xoff LT 0 THEN xoff = 0
               IF yoff LT 0 THEN yoff = 0
               WIDGET_CONTROL, draw_state.work_view, SET_DRAW_VIEW=[xoff, yoff]
               WIDGET_CONTROL, draw_state.work_view, SET_UVALUE=workstate, $
                  /NO_COPY
            ENDIF
         END

         ELSE : BEGIN
         END
      ENDCASE

      WIDGET_CONTROL, event.id, SET_UVALUE=fullstate, /NO_COPY
      ;Don't want to do anything else after the full-view event.
      WIDGET_CONTROL, event.handler, SET_UVALUE=draw_state, /NO_COPY
      RETURN, 0
   END

   draw_state.work_view : BEGIN
      WIDGET_CONTROL, event.id, GET_UVALUE=workstate, /NO_COPY

      IF !order NE 0 THEN $
         eventy = workstate.ysize-event.y-1 $
      ELSE $
         eventy = event.y

      IF workstate.zfact NE 1 THEN BEGIN
         x = workstate.spx + ( event.x - workstate.xoff ) / workstate.zfact
         y = workstate.spy + ( eventy - workstate.yoff ) / workstate.zfact
      ENDIF ELSE BEGIN
         x = workstate.spx + ( event.x - workstate.xoff )
         y = workstate.spy + ( eventy - workstate.yoff )
      ENDELSE

      WIDGET_CONTROL, event.id, SET_UVALUE=workstate, /NO_COPY
   END

   draw_state.zoom_view : BEGIN
      WIDGET_CONTROL, event.id, GET_UVALUE=zoomstate, /NO_COPY

      IF !order NE 0 THEN $
         eventy = zoomstate.ysize-event.y-1 $
      ELSE $
         eventy = event.y

      IF zoomstate.zfact NE 1 THEN BEGIN
         x = zoomstate.spx + ( event.x - zoomstate.xoff ) / zoomstate.zfact
         y = zoomstate.spy + ( eventy - zoomstate.yoff ) / zoomstate.zfact
      ENDIF ELSE BEGIN
         x = zoomstate.spx + ( event.x - zoomstate.xoff )
         y = zoomstate.spy + ( eventy - zoomstate.yoff )
      ENDELSE

      WIDGET_CONTROL, event.id, SET_UVALUE=zoomstate, /NO_COPY
   END

   ELSE : BEGIN
      ;This was an event from one of the Zoom+ or Zoom- buttons. Restore
      ;the image state and pass the event up to the next highest event
      ;handler (cw_itool_eve).
      WIDGET_CONTROL, event.handler, SET_UVALUE=draw_state, /NO_COPY
      RETURN, event
   END
ENDCASE

;Check for x,y out of array bounds. This is necessary, because the draw
;windows may be larger than requested.
WIDGET_CONTROL, draw_state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
IF x GE im_parms.xsize THEN x=im_parms.xsize-1
IF y GE im_parms.ysize THEN y=im_parms.ysize-1
WIDGET_CONTROL, draw_state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY

;At this point, x and y have been transformed to the actual image array
;coordinates (from the work or zoom draw window).

CASE event.type OF
   0 : BEGIN
      ;Mouse button pressed.
      CASE event.press OF
         1 : BEGIN
            ;Left Button (template or comet photometry).
            WIDGET_CONTROL, draw_state.tpmgrbase, GET_UVALUE=tpstatus
            WIDGET_CONTROL, draw_state.cpmgrbase, GET_UVALUE=cpstatus

            IF tpstatus.active EQ 1B THEN BEGIN
               ;Template photometry.
               cw_itool_phact, draw_state, x, y
;               WIDGET_CONTROL, draw_state.zoom_view, GET_UVALUE=zoomstate, $
;                  /NO_COPY
;               WIDGET_CONTROL, draw_state.imparmsbase, GET_UVALUE=im_parms, $
;                  /NO_COPY
;               cw_itool_trk, draw_state, zoomstate.xcen, zoomstate.ycen
;               im_parms.lasttype=2
;               im_parms.lastpos=[zoomstate.xcen,zoomstate.ycen]
;               WIDGET_CONTROL, draw_state.imparmsbase, SET_UVALUE=im_parms, $
;                  /NO_COPY
;               WIDGET_CONTROL, draw_state.zoom_view, SET_UVALUE=zoomstate, $
;                  /NO_COPY
            ENDIF

            IF cpstatus.active EQ 1B THEN BEGIN
               ;Comet photometry.
               cpmgrid = WIDGET_INFO( draw_state.cpmgrbase, /CHILD )
               ;Pass the x,y coordinates to thw Comet Photometry Manager.
               WIDGET_CONTROL, cpmgrid, SET_VALUE={x:x, y:y}
               WIDGET_CONTROL, draw_state.imparmsbase, GET_UVALUE=im_parms, $
                  /NO_COPY
               im_parms.lasttype=0
               WIDGET_CONTROL, draw_state.imparmsbase, SET_UVALUE=im_parms, $
                  /NO_COPY
            ENDIF
         END

         2 : BEGIN
            ;Middle button (zoom at new center).
            cw_itool_draw, draw_state, /ZOOM, ZXCEN=x, ZYCEN=y
            WIDGET_CONTROL, draw_state.zoom_view, GET_UVALUE=zoomstate, $
               /NO_COPY
            cw_itool_trk, draw_state, zoomstate.xcen, zoomstate.ycen
            WIDGET_CONTROL, draw_state.zoom_view, SET_UVALUE=zoomstate, $
               /NO_COPY
         END

         4 : BEGIN

            ;Right button (single object photometry).
            cw_itool_oneph,draw_state,x,y

         END

         ELSE : BEGIN
         END
      ENDCASE
   END

   2 : BEGIN
      ;Motion event.
      IF draw_state.trkflg THEN BEGIN
         ;Refresh the cursor tracking widgets.
         cw_itool_trk, draw_state, x, y
      ENDIF

      IF event.press EQ 2 THEN BEGIN
         ;Re-draw the zoom window at the new center.
         cw_itool_draw, draw_state, /ZOOM, ZXCEN=x, ZYCEN=y
      ENDIF
   END

   ELSE : BEGIN
   END
ENDCASE

WIDGET_CONTROL, event.handler, SET_UVALUE=draw_state, /NO_COPY

RETURN, out_event
END


; ------------------------------------------------------------------------------
; Procedure cw_pixed_exteve
;
;   This is the external event handler for cw_pixed. It is designed to
; support events from a compound widget which 'hangs' on a Top Level Base.
;   The method used differs from the usual in that the 'state' structure
; is stored in the user-value of the TLB instead of in the first child
; base.
; ------------------------------------------------------------------------------
PRO cw_pixed_exteve, event

;The structure contained in the TLB user-value is:
;   {imparmsbase:0L, work_view:0L, zoom_view:0L}
;These tags are copies of pointers within cw_itool. These allow access
;to data which are needed to update the work and zoom draw windows with
;the modified pixel values.

WIDGET_CONTROL, event.top, GET_UVALUE=topstate, /NO_COPY

WIDGET_CONTROL, topstate.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
WIDGET_CONTROL, im_parms.imageptr, GET_UVALUE=image, /NO_COPY
WIDGET_CONTROL, topstate.work_view, GET_UVALUE=workstate, /NO_COPY
WIDGET_CONTROL, topstate.work_view, GET_VALUE=work_win
WIDGET_CONTROL, topstate.zoom_view, GET_UVALUE=zoomstate, /NO_COPY
WIDGET_CONTROL, topstate.zoom_view, GET_VALUE=zoom_win
curmin = im_parms.curmin[im_parms.frame]
curmax = im_parms.curmax[im_parms.frame]

CASE event.type OF
   1 : BEGIN
      FOR j=0, event.count-1 DO BEGIN
         image[ event.x[j], event.y[j], im_parms.frame ] = event.value[j]

         t = REBIN([event.value[j]], zoomstate.zfact, zoomstate.zfact, /SAMPLE)
         t = BYTSCL( t, MIN=curmin, MAX=curmax, TOP=!d.n_colors-1 )
         dx = event.x[j] - zoomstate.spx
         dy = event.y[j] - zoomstate.spy
         WSET, zoom_win
         TV, t, zoomstate.xoff+dx*zoomstate.zfact, $
                zoomstate.yoff+dy*zoomstate.zfact

         t = REBIN([event.value[j]], workstate.zfact, workstate.zfact, /SAMPLE)
         t = BYTSCL( t, MIN=curmin, MAX=curmax, TOP=!d.n_colors-1 )
         dx = event.x[j] * workstate.zfact
         dy = event.y[j] * workstate.zfact
         WSET, work_win
         TV, t, dx, dy
      ENDFOR
   END

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

WIDGET_CONTROL, topstate.zoom_view, SET_UVALUE=zoomstate, /NO_COPY
WIDGET_CONTROL, topstate.work_view, SET_UVALUE=workstate, /NO_COPY
WIDGET_CONTROL, im_parms.imageptr, SET_UVALUE=image, /NO_COPY
WIDGET_CONTROL, topstate.imparmsbase, SET_UVALUE=im_parms, /NO_COPY

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


; ------------------------------------------------------------------------------
; Function cw_itool_eve
; Event handler for cw_itool main procedure.
; ------------------------------------------------------------------------------
FUNCTION cw_itool_eve, event
WIDGET_CONTROL, event.id, /HOURGLASS

bel = STRING( 7B )

out_event = 0

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

CASE event.id OF
   state.animateid : BEGIN
      ;Start animation procedure.
      WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
      imfile = im_parms.imfile
      nframes = im_parms.nframes
      xsize = im_parms.xsize
      ysize = im_parms.ysize
      WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY

      WIDGET_CONTROL, state.work_view, GET_UVALUE=workstate, /NO_COPY
      xs = workstate.zfact * xsize
      ys = workstate.zfact * ysize
      WIDGET_CONTROL, state.work_view, SET_UVALUE=workstate, /NO_COPY

      title = 'Itool Animation'
      IF imfile NE '' THEN title = title + ' - image file ' + imfile
      xinteranimate, GROUP=event.top, SET=[xs, ys, nframes], /SHOWLOAD, $
                     TITLE=title

      FOR j=0, nframes-1 DO BEGIN
         WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
         IF im_parms.asis[j] EQ 0 THEN cw_itool_cstr, im_parms, j, /SILENT
         WIDGET_CONTROL, im_parms.imageptr, GET_UVALUE=image, /NO_COPY
         xinteranimate, FRAME=j, GROUP=event.top, IMAGE=$
            BYTSCL(REBIN(image[*,*,j], xs, ys, /SAMPLE), $
               MIN=im_parms.curmin[j], MAX=im_parms.curmax[j], $
               TOP=!D.N_COLORS-1 )
         WIDGET_CONTROL, im_parms.imageptr, SET_UVALUE=image, /NO_COPY
         WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY
      ENDFOR

      xinteranimate, 50, GROUP=event.top

   END

   state.auto1id : BEGIN
      ;Set display stretch to computed values (current frame).

      WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
      frame = im_parms.frame
      cw_itool_cstr, im_parms, frame, /SILENT
      im_parms.asis[frame] = 0B
      im_parms.curmin[frame] = im_parms.sclmin[frame]
      im_parms.curmax[frame] = im_parms.sclmax[frame]
      WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY

      ;Refresh all of the widgets.
      cw_itool_disp, state
      WIDGET_CONTROL, state.drawbase, GET_UVALUE=draw_state, /NO_COPY
      cw_itool_draw, draw_state, /FULL, /ZOOM, /WORK
      WIDGET_CONTROL, state.drawbase, SET_UVALUE=draw_state, /NO_COPY

   END

   state.auto2id : BEGIN
      ;Set display stretch to computed values (all frames).

      WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
      FOR frame=0, im_parms.nframes-1 DO BEGIN
         cw_itool_cstr, im_parms, frame, /SILENT
         im_parms.asis[frame]   = 0B
         im_parms.curmin[frame] = im_parms.sclmin[frame]
         im_parms.curmax[frame] = im_parms.sclmax[frame]
      ENDFOR
      WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY

      ;Refresh all widgets.
      cw_itool_disp, state
      WIDGET_CONTROL, state.drawbase, GET_UVALUE=draw_state, /NO_COPY
      cw_itool_draw, draw_state, /FULL, /ZOOM, /WORK
      WIDGET_CONTROL, state.drawbase, SET_UVALUE=draw_state, /NO_COPY

   END

   state.colpalid : BEGIN
      xpalette, GROUP=event.top
   END

   state.coltabid : BEGIN
      xloadct, GROUP=event.top
   END

   state.cpmgrid : BEGIN
      WIDGET_CONTROL, state.tpmgrbase, GET_UVALUE=tpstatus

      IF tpstatus.active EQ 0B THEN BEGIN
         ;All clear to start.
         ;Set the active flag.
         WIDGET_CONTROL, state.cpmgrbase, GET_UVALUE=status, /NO_COPY
         status.active = 1B
         WIDGET_CONTROL, state.cpmgrbase, SET_UVALUE=status, /NO_COPY

         ;Get image pointer.
         WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
         imageptr = im_parms.imageptr
         WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY

         IF NOT WIDGET_INFO( state.cpmgrbase, /REALIZED ) THEN BEGIN
            w1 = cw_cpmgr( state.cpmgrbase, IMAGEPTR=imageptr, $
                           IMPARMSPTR=state.imparmsbase, $
                           PHPARMSPTR=state.phparmsbase )
            WIDGET_CONTROL, state.cpmgrbase, /REALIZE
         ENDIF
         WIDGET_CONTROL, state.cpmgrbase, MAP=1
      ENDIF ELSE BEGIN
         MESSAGE, 'Error. Template Photometry is active.' + bel, /INFO
      ENDELSE
   END

   state.disp2id : BEGIN
      ;Copy display stretch to all frames.

      WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
      frame = im_parms.frame
      FOR j=0, im_parms.nframes-1 DO BEGIN
         im_parms.curmin[j] = im_parms.curmin[frame]
         im_parms.curmax[j] = im_parms.curmax[frame]
         im_parms.asis[j] = 1B
      ENDFOR
      WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY

      ;Refresh all widgets.
      cw_itool_disp, state
      WIDGET_CONTROL, state.drawbase, GET_UVALUE=draw_state, /NO_COPY
      cw_itool_draw, draw_state, /FULL, /ZOOM, /WORK
      WIDGET_CONTROL, state.drawbase, SET_UVALUE=draw_state, /NO_COPY
   END

   state.dispmaxid : BEGIN
      WIDGET_CONTROL, event.id, GET_VALUE=value

      WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
      im_parms.curmax[im_parms.frame] = value[0]
      im_parms.asis[im_parms.frame] = 1B
      WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY

      ;Refresh all widgets.
      cw_itool_disp, state
      WIDGET_CONTROL, state.drawbase, GET_UVALUE=draw_state, /NO_COPY
      cw_itool_draw, draw_state, /FULL, /ZOOM, /WORK
      WIDGET_CONTROL, state.drawbase, SET_UVALUE=draw_state, /NO_COPY
   END

   state.dispminid : BEGIN
      WIDGET_CONTROL, event.id, GET_VALUE=value

      WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
      im_parms.curmin[im_parms.frame] = value[0]
      im_parms.asis[im_parms.frame] = 1B
      WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY

      ;Refresh all widgets.
      cw_itool_disp, state
      WIDGET_CONTROL, state.drawbase, GET_UVALUE=draw_state, /NO_COPY
      cw_itool_draw, draw_state, /FULL, /ZOOM, /WORK
      WIDGET_CONTROL, state.drawbase, SET_UVALUE=draw_state, /NO_COPY
   END

   state.dismissid : BEGIN
      WIDGET_CONTROL, event.top, MAP=0
   END

   state.extrema1id : BEGIN
      ;Set display stretch to image extrema (current frame).

      WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
      j = im_parms.frame
      im_parms.curmin[j] = im_parms.minvalue[j]
      im_parms.curmax[j] = im_parms.maxvalue[j]
      im_parms.asis[j] = 1B
      WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY

      ;Refresh all widgets.
      cw_itool_disp, state
      WIDGET_CONTROL, state.drawbase, GET_UVALUE=draw_state, /NO_COPY
      cw_itool_draw, draw_state, /FULL, /ZOOM, /WORK
      WIDGET_CONTROL, state.drawbase, SET_UVALUE=draw_state, /NO_COPY
   END

   state.extrema2id : BEGIN
      ;Set display stretch to image extrema (all frames).

      WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
      FOR j=0, im_parms.nframes-1 DO BEGIN
         cw_itool_cstr, im_parms, j, /SILENT
         im_parms.curmin[j] = im_parms.minvalue[j]
         im_parms.curmax[j] = im_parms.maxvalue[j]
         im_parms.asis[j] = 1B
      ENDFOR
      WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY

      ;Refresh all widgets.
      cw_itool_disp, state
      WIDGET_CONTROL, state.drawbase, GET_UVALUE=draw_state, /NO_COPY
      cw_itool_draw, draw_state, /FULL, /ZOOM, /WORK
      WIDGET_CONTROL, state.drawbase, SET_UVALUE=draw_state, /NO_COPY
   END

   state.frameid : BEGIN
      WIDGET_CONTROL, event.id, GET_VALUE=value

      ;Requested frame.
      frame = LONG( value[0] )

      ;Get total number of frames.
      WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
      nframes = im_parms.nframes
      WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY

      IF ( frame GE 0 ) AND ( frame LE nframes-1 ) THEN BEGIN
         ;The requested frame is legal.
         WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
         im_parms.frame = frame
         WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY
         cw_itool_disp, state
         WIDGET_CONTROL, state.drawbase, GET_UVALUE=draw_state, /NO_COPY
         cw_itool_draw, draw_state, /FULL, /ZOOM, /WORK
         WIDGET_CONTROL, state.drawbase, SET_UVALUE=draw_state, /NO_COPY
      ENDIF ELSE BEGIN
         ;The requested frame is bunk. Put the current frame number back
         ;into the text widget.
         WIDGET_CONTROL, state.frameid, SET_VALUE=STRING( im_parms.frame, $
                         FORMAT=state.fmti )
      ENDELSE
   END

   state.hdcpyid : BEGIN
      ; Make a hard copy of the current image.
      WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
      WIDGET_CONTROL, im_parms.imageptr, GET_UVALUE=image, /NO_COPY

      frame = im_parms.frame
      title = im_parms.object + '  ' + im_parms.imfile + '  ' + im_parms.ut
      hardim, image[*,*,frame], im_parms.curmin[frame], $
      im_parms.curmax[frame], TITLE=title, WIDTH=18, AUTOSIZE=1, /negative

      WIDGET_CONTROL, im_parms.imageptr, SET_UVALUE=image, /NO_COPY
      WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY
   END

   state.imparmsid : BEGIN
      IF WIDGET_INFO( state.ipmgrid, /VALID_ID ) EQ 0 THEN BEGIN
         ;The image parameters widget is inactive.
         state.ipmgrid = cw_ipmgr( state.imparmsbase )
         WIDGET_CONTROL, state.imparmsbase, /REALIZE, /MAP
      ENDIF
   END

   state.nextframeid : BEGIN
      WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
      im_parms.frame = (im_parms.frame+1) MOD im_parms.nframes
      WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY

      ;Refresh all widgets.
      cw_itool_disp, state
      WIDGET_CONTROL, state.drawbase, GET_UVALUE=draw_state, /NO_COPY
      cw_itool_draw, draw_state, /FULL, /ZOOM, /WORK
      WIDGET_CONTROL, state.drawbase, SET_UVALUE=draw_state, /NO_COPY
   END

   state.pfilesid : BEGIN
      ;Retrive some structures.
      WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
      WIDGET_CONTROL, im_parms.imageptr, GET_UVALUE=image, /NO_COPY
      WIDGET_CONTROL, state.phparmsbase, GET_UVALUE=ph_parms, /NO_COPY
      WIDGET_CONTROL, state.zoom_view, GET_UVALUE=zoomstate, /NO_COPY

      ;Get the set-point and size of the zoom-window piece of the image.
      xs = zoomstate.spx
      ys = zoomstate.spy
      dx = zoomstate.dx
      dy = zoomstate.dy

      ;Piece together the title for the TLB.
      title = ''
      IF (im_parms.object NE '') THEN title = im_parms.object
      IF (im_parms.imfile NE '') THEN title = title +'  '+ im_parms.imfile
      IF(im_parms.ut NE '') THEN title = title + ' ' + im_parms.ut

      ;Extract the zoomed piece.
      array = image[xs:xs+dx-1, ys:ys+dy-1, im_parms.frame]

      ;Get the Plate Scale.
      pscale = ph_parms.pscale

      ;Restore the structures.
      WIDGET_CONTROL, state.zoom_view, SET_UVALUE=zoomstate, /NO_COPY
      WIDGET_CONTROL, state.phparmsbase, SET_UVALUE=ph_parms, /NO_COPY
      WIDGET_CONTROL, im_parms.imageptr, SET_UVALUE=image, /NO_COPY
      WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY

      ;Get the list of TLB instances.
      WIDGET_CONTROL, state.pfilesid, GET_UVALUE=instances

      ;Check to see if any of the existing slots have a profiles widget
      ;active (valid TLB).
      active = WIDGET_INFO( instances, /VALID_ID )

      f = WHERE( active EQ 0L, count )
      IF count NE 0 THEN BEGIN
         ;At least one of the slots is available for use. Take the
         ;first available.
         base = WIDGET_BASE( TITLE='Itool Profiles ' + title )
         XMANAGER, '', base, GROUP_LEADER=state.mainbase, /JUST_REG
         instances[f] = base
      ENDIF ELSE BEGIN
         ;All of the slots are currently active. Add a new slot.
         base = WIDGET_BASE( TITLE='Itool Profiles ' + title )
         XMANAGER, '', base, GROUP_LEADER=state.mainbase, /JUST_REG
         instances = [ instances, base ]
      ENDELSE

      ;Put the instances vector back into the user-value of the holding base.
      WIDGET_CONTROL, state.pfilesid, SET_UVALUE=instances

      pfbase = cw_pfile( base, PLATESCALE=pscale, HCTITLE=title )

      WIDGET_CONTROL, base, /REALIZE, /MAP

      ;Use the set_value call to display the profile.
      WIDGET_CONTROL, pfbase, SET_VALUE={image:array, xset:xs, yset:ys}
   END

   state.phparmsid : BEGIN
      child = WIDGET_INFO( state.phparmsbase, /CHILD )
      IF child EQ 0L THEN BEGIN
         ;The photometry parameters widget is inactive.
         state.ppmgrid = cw_ppmgr( state.phparmsbase )
         WIDGET_CONTROL, state.phparmsbase, /REALIZE, /MAP
      ENDIF
   END

   state.pixedid : BEGIN
      child = WIDGET_INFO( state.pixedbase, /CHILD )
      IF child EQ 0L THEN BEGIN
         ;Pixel editor is not active. Invoke it.
         WIDGET_CONTROL, state.imparmsbase, GET_UVALUE=im_parms, /NO_COPY
         WIDGET_CONTROL, im_parms.imageptr, GET_UVALUE=image, /NO_COPY
         WIDGET_CONTROL, state.zoom_view, GET_UVALUE=zoomstate, /NO_COPY

         state.pixedevid = cw_pixed( state.pixedbase, image, $
            zoomstate.xcen, zoomstate.ycen )
         WIDGET_CONTROL, state.pixedbase, /REALIZE, /MAP

         WIDGET_CONTROL, state.zoom_view, SET_UVALUE=zoomstate, /NO_COPY
         WIDGET_CONTROL, im_parms.imageptr, SET_UVALUE=image, /NO_COPY
         WIDGET_CONTROL, state.imparmsbase, SET_UVALUE=im_parms, /NO_COPY
      ENDIF
   END

   state.tmplmgrid : BEGIN
      WIDGET_CONTROL, state.cpmgrbase, GET_UVALUE=cpstatus
      IF cpstatus.active EQ 0B THEN BEGIN
         ;All clear to start. Set the active flag.
         WIDGET_CONTROL, state.tpmgrbase, GET_UVALUE=status, /NO_COPY
         status.active = 1B
         WIDGET_CONTROL, state.tpmgrbase, SET_UVALUE=status, /NO_COPY

         IF NOT WIDGET_INFO( state.tpmgrbase, /REALIZED ) THEN BEGIN
            base = cw_tpmgr( state.tpmgrbase, TMPLFILE=state.tmplfile )
            WIDGET_CONTROL, state.tpmgrbase, /REALIZE
         ENDIF

         WIDGET_CONTROL, state.tpmgrbase, MAP=1
      ENDIF ELSE BEGIN
         MESSAGE, 'Error. Comet Photometry is active.' + bel, /INFO
      ENDELSE
   END

   state.trkid : BEGIN
      WIDGET_CONTROL, state.drawbase, GET_UVALUE=draw_state, /NO_COPY
      CASE event.value OF
         0 : draw_state.trkflg = 0
         1 : draw_state.trkflg = 1
         ELSE : MESSAGE, '(tracking) unknown event.nt.', /INFO
      ENDCASE
      WIDGET_CONTROL, state.drawbase, SET_UVALUE=draw_state, /NO_COPY
   END

   state.zoomdnid : BEGIN
      WIDGET_CONTROL, state.zoom_view, GET_UVALUE=zoomstate
      IF zoomstate.zfact GT 1 THEN BEGIN
         zoomstate.zfact = zoomstate.zfact - 1
         WIDGET_CONTROL, state.zoom_view, SET_UVALUE=zoomstate
         newzoom = STRING( zoomstate.zfact, FORMAT='(I3)' )
         WIDGET_CONTROL, state.zoomvid, SET_VALUE=newzoom
         WIDGET_CONTROL, state.drawbase, GET_UVALUE=draw_state, /NO_COPY
         cw_itool_draw, draw_state, /ZOOM
         WIDGET_CONTROL, state.drawbase, SET_UVALUE=draw_state, /NO_COPY
      ENDIF
   END

   state.zoomupid : BEGIN
      WIDGET_CONTROL, state.zoom_view, GET_UVALUE=zoomstate
      zoomstate.zfact = zoomstate.zfact + 1
      WIDGET_CONTROL, state.zoom_view, SET_UVALUE=zoomstate
      newzoom = STRING( zoomstate.zfact, FORMAT='(I3)' )
      WIDGET_CONTROL, state.zoomvid, SET_VALUE=newzoom
      WIDGET_CONTROL, state.drawbase, GET_UVALUE=draw_state, /NO_COPY
      cw_itool_draw, draw_state, /ZOOM
      WIDGET_CONTROL, state.drawbase, SET_UVALUE=draw_state, /NO_COPY
   END

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


WIDGET_CONTROL, stash, SET_UVALUE=state, /NO_COPY

RETURN, out_event

END


; ------------------------------------------------------------------------------
; Function cw_itool
; ------------------------------------------------------------------------------
FUNCTION cw_itool, parent, FVISIBLE=fvisible, NODISMISS=nodismiss, $
         PHOTPARMFILE=photparmfile, $
         TMPLFILE=tmplfile, $
         XSIZE=xsize, YSIZE=ysize, WXVISIBLE=wxvisible, WYVISIBLE=wyvisible, $
         WZOOMFACT=wzoomfact, $
         ZVISIBLE=zvisible

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

DEVICE,GET_SCREEN_SIZE=scrsize
IF scrsize[0] GT 1024 THEN BEGIN
   xvisdef = 900
   yvisdef = 640
ENDIF ELSE IF scrsize[0] GT 768 THEN BEGIN
   xvisdef = 500
   yvisdef = 500
ENDIF ELSE BEGIN
   xvisdef = 400
   yvisdef = 400
ENDELSE

IF (NOT KEYWORD_SET( xsize )) OR (NOT KEYWORD_SET( ysize )) THEN BEGIN
   MESSAGE, 'Keywords XSIZE and YSIZE must be specified.', /INFO
   RETURN, 0L
ENDIF

IF NOT KEYWORD_SET( fvisible ) THEN fvisible=128
IF fvisible EQ 0 THEN fvisible=128

IF NOT KEYWORD_SET( wxvisible ) THEN wxvisible=xvisdef
IF wxvisible EQ 0 THEN wxvisible=xvisdef

IF NOT KEYWORD_SET( wyvisible ) THEN wyvisible=yvisdef
IF wyvisible EQ 0 THEN wyvisible=yvisdef

IF NOT KEYWORD_SET( zvisible ) THEN zvisible=128
IF zvisible EQ 0 THEN zvisible=128

; Initialize the main-state control structure.
state = { $
   mainbase:0L, $                 ; Itool base id.
   animateid:0L, $                ; Animate button id.
   auto1id:0L, auto2id:0L, $      ; Stretch control menu button id.
   colpalid:0L, coltabid:0L, $
   cpmgrbase:0L, $
   cpmgrid:0L, $
   debugid:0L, $
   disp2id:0L, $                  ; Stretch control menu button id.
   dispminid:0L, dispmaxid:0L, $  ; Widget id's for stretch min and max.
   drawbase:0L, $
   dismissid:0L, $
   extrema1id:0L, $
   extrema2id:0L, $
   fmtg:'(G12.6)', $              ; Image info format.
   fmti:'(I12)', $                ; Image info format.
   font:'8x13', $                 ; Font for some label and text widgets.
   frameid:0L, $                  ; Frame number widget id.
   full_view:0L, $
   hdcpyid:0L, $
   imageminid:0L, imagemaxid:0L, $
   imfileid:0L, $                 ; Image file name widget id.
   imparmsbase:0L, $
   imparmsid:0L, $
   ipmgrid:0L, $
   nextframeid:0L, $              ; ID of NextFrame button.
   nframesid:0L, $
   objectid:0L, $                 ; Object label widget id.
   filterid:0L, $                 ; Filter label widget id.
   airmasid:0L, $                 ; Airmass label widget id.
   exptimid:0L, $                 ; Exposure time label widget id.
   utdateid:0L, $                 ; UT Date label widget id.
   uttimeid:0L, $                 ; UT Time label widget id.
   parent:parent, $
   pfilesid:0L, $
   phparmsbase:0L, $
   phparmsid:0L, $
   pixedbase:0L, $                ; Pixel Editor TLB.
   pixedevid:0L, $
   pixedid:0L, $
   ppmgrid:0L, $
   savplt:!p, $                   ; Save area for plotting environment.
   scroll:0B, $
   tpmgrbase:0L, $
   tempedit:0, $                  ; Template edit flag.
   tmplfile:'', $                 ; Name of template manager file.
   tmplmgrid:0L, $
   trkid:0L, $
   workbase:0L, $
   work_view:0L, $
   zoomupid:0L, $
   zoomdnid:0L, $
   zoomvid:0L, $
   zoom_view:0L }

;Initialize the draw state structure.
draw_state = { $
   cpmgrbase:0L, $
   curposid:0L, $
   full_view:0L, $
   imparmsbase:0L, $
   mainstash:0L, $
   pfilesid:0L, $
   phparmsbase:0L, $
   scroll:0B, $
   tpmgrbase:0L, $
   trkflg:0B, $
   work_view:0L, $
   zoom_view:0L }

; Initialize the photometry parameters structure.
ph_parms = { $
   boxmrad:10.0, $
   edtflg:0B, $
   exact:0, $
   gain:1.0, $
   logfile:'phot.log', $
   nomext:0.0, $
   objnum:0, $
   parmfile:'', $
   parmfilever:'phot_parms_v01', $
   pscale:0.726, $
   radius:5.0, $
   sky1:10.0, $
   sky2:50.0,$
   zpoint:0.0 }

; *****************************************************************************
IF KEYWORD_SET( photparmfile ) THEN ph_parms.parmfile=photparmfile

IF KEYWORD_SET( tmplfile ) THEN state.tmplfile=tmplfile

; Create the main base.
;
IF KEYWORD_SET( in_uvalue ) THEN BEGIN
   mainbase = WIDGET_BASE( parent, COLUMN=1, EVENT_FUNC='cw_itool_eve', $
              FUNC_GET_VALUE='cw_itool_gvl', PRO_SET_VALUE='cw_itool_svl', $
              UVALUE=in_uvalue )
ENDIF ELSE BEGIN
   mainbase = WIDGET_BASE( parent, COLUMN=1, EVENT_FUNC='cw_itool_eve', $
              FUNC_GET_VALUE='cw_itool_gvl', PRO_SET_VALUE='cw_itool_svl' )
ENDELSE

state.mainbase = mainbase

;Define the top-level bases for itool applications.
state.imparmsbase = WIDGET_BASE( TITLE='Itool Image Parameters', $
                    GROUP_LEADER=parent )
XMANAGER, '', state.imparmsbase, /JUST_REG
draw_state.imparmsbase = state.imparmsbase

state.phparmsbase = WIDGET_BASE( TITLE='Itool Photometry Parameters', $
                    GROUP_LEADER=parent )
XMANAGER, '', state.phparmsbase, /JUST_REG
draw_state.phparmsbase = state.phparmsbase

state.cpmgrbase = WIDGET_BASE( TITLE='Itool Comet Photometry Manager', $
                    GROUP_LEADER=parent, UVALUE={active:0B} )
XMANAGER, '', state.cpmgrbase, /JUST_REG
draw_state.cpmgrbase = state.cpmgrbase

state.tpmgrbase = WIDGET_BASE( TITLE='Itool Photometry Template Manager', $
                    GROUP_LEADER=parent, UVALUE={active:0B} )
XMANAGER, '', state.tpmgrbase, EVENT_HANDLER='cw_itool_tpeve', /JUST_REG
draw_state.tpmgrbase = state.tpmgrbase

;This is the Top Level Base for the Pixel Editor. Events arriving from the
;Pixel Editor compound widget are routed to the handler associated with this
;base. These events have an integer field called 'type.' A value of 0  is
;returned if the Exit button was pressed. A value of 1 means update the
;pixel value at specified location.
state.pixedbase = WIDGET_BASE( TITLE='Itool Zoom-Window Pixel Editor' )
XMANAGER, '', state.pixedbase, EVENT_HANDLER='cw_pixed_exteve', $
          GROUP_LEADER=mainbase, /JUST_REG

; Main button section.
mbbase = WIDGET_BASE( mainbase, COLUMN=5 )
; ---------------------
state.phparmsid = WIDGET_BUTTON( mbbase, VALUE='Photometry Params' )
state.cpmgrid = WIDGET_BUTTON( mbbase, VALUE='Comet Phot Mgr' )
; ---------------------
state.imparmsid = WIDGET_BUTTON( mbbase, VALUE='Image Params' )
state.tmplmgrid = WIDGET_BUTTON( mbbase, VALUE='Template Mgr' )
; ---------------------
;This base is the holding area for the Top Level Bases used to manage
;one, or more, copies of the Profiles widget. The TLB's are stored in
;a longword vector and placed in the user-value.
state.pfilesid = WIDGET_BUTTON( mbbase, VALUE='Profiles', UVALUE=[0L] )
draw_state.pfilesid = state.pfilesid
state.pixedid = WIDGET_BUTTON( mbbase, VALUE='Pixel Editor' )
; ---------------------
state.coltabid = WIDGET_BUTTON( mbbase, VALUE='Color Table' )
state.colpalid = WIDGET_BUTTON( mbbase, VALUE='Edit Palette' )
; ---------------------
state.hdcpyid = WIDGET_BUTTON( mbbase, VALUE='Hard Copy' )
state.dismissid  = WIDGET_BUTTON( mbbase, VALUE='Dismiss' )
IF KEYWORD_SET( nodismiss ) THEN BEGIN
   WIDGET_CONTROL, state.dismissid, SENSITIVE=0
ENDIF
; ---------------------

;state.debugid = WIDGET_BUTTON( mbbase, VALUE='Debug' )

;Image stats section.
maxvalue = STRING( 0.0, FORMAT=state.fmtg )
minvalue = STRING( 0.0, FORMAT=state.fmtg )
curmax   = STRING( 0.0, FORMAT=state.fmtg )
curmin   = STRING( 0.0, FORMAT=state.fmtg )
nframes  = STRING( 0L,  FORMAT=state.fmti )
framen   = STRING( 0L,  FORMAT=state.fmti )

imstatbase = WIDGET_BASE( mainbase, ROW=1 )
wbc = WIDGET_BASE( imstatbase, COLUMN=2, FRAME=1 )
; ---------------------
w1  = WIDGET_LABEL( wbc, VALUE='Image Max:   ' )
w1  = WIDGET_LABEL( wbc, VALUE='Image Min:   ' )
w1  = WIDGET_LABEL( wbc, VALUE='Total Frames:' )
; ---------------------
w1  = WIDGET_LABEL( wbc, VALUE=maxvalue )
state.imagemaxid = w1
w1  = WIDGET_LABEL( wbc, VALUE=minvalue )
state.imageminid = w1
w1  = WIDGET_LABEL( wbc, VALUE=nframes )
state.nframesid = w1

; ---------------------
wbc = WIDGET_BASE( imstatbase, COLUMN=3, FRAME=1 )
w1  = WIDGET_LABEL( wbc, VALUE='Display Max:  ' )
w1  = WIDGET_LABEL( wbc, VALUE='Display Min:  ' )
w1  = WIDGET_LABEL( wbc, VALUE='Display Frame:' )
; ---------------------
state.dispmaxid = WIDGET_TEXT( wbc,  VALUE=curmax, /EDITABLE, XSIZE=12 )
state.dispminid = WIDGET_TEXT( wbc,  VALUE=curmin, /EDITABLE, XSIZE=12 )
state.frameid   = WIDGET_TEXT( wbc,  VALUE=framen, /EDITABLE, XSIZE=12 )
WIDGET_CONTROL, state.frameid, SENSITIVE=0
; ---------------------

;Stretch control pull-down menu.
wbr = wbc
w1  = WIDGET_BUTTON( wbr, VALUE='Stretch Menu', MENU=2 )
state.auto1id = WIDGET_BUTTON( w1, $
      VALUE='Set display stretch to computed values (current frame)' )
state.extrema1id = WIDGET_BUTTON( w1, $
      VALUE='Set display stretch to image extrema (current frame)' )
state.extrema2id = WIDGET_BUTTON( w1, $
      VALUE='Set display stretch to image extrema (all frames)' )
state.auto2id = WIDGET_BUTTON( w1, $
      VALUE='Set display stretch to computed values (all frames)' )
state.disp2id = WIDGET_BUTTON( w1, VALUE='Copy display stretch to all frames' )

state.animateid = WIDGET_BUTTON( wbr, VALUE='Animation' )
state.nextframeid = WIDGET_BUTTON( wbr, VALUE='Next Frame' )

; scan through all the items in this base and find the max x and y sizes.
max_xs=0
max_ys=0
t_b = WIDGET_INFO(wbc, /CHILD)  ; gets first child
WHILE (t_b NE 0L) DO BEGIN
   geo = WIDGET_INFO(t_b, /GEOMETRY)
   max_xs=max([max_xs,geo.scr_xsize])
   max_ys=max([max_ys,geo.scr_ysize])
   t_b = WIDGET_INFO(t_b, /SIBLING) ; gets next child (0=no more)
ENDWHILE

t_b = WIDGET_INFO(wbc, /CHILD)
WHILE (t_b NE 0L) DO BEGIN
   WIDGET_CONTROL, t_b, SCR_XSIZE=max_xs, SCR_YSIZE=max_ys
   t_b = WIDGET_INFO(t_b, /SIBLING)
ENDWHILE

; Cursor-tracking base.
wb  = WIDGET_BASE( mainbase, /ROW, /FRAME )
w1  = WIDGET_LABEL( wb, VALUE='Cursor: ' )
state.trkid  = CW_BGROUP( wb, ['Freeze','Track' ], /EXCLUSIVE, /NO_RELEASE, $
      /ROW, SET_VALUE=draw_state.trkflg )
draw_state.curposid = WIDGET_LABEL( wb, VALUE='', /DYNAMIC_RESIZE )

;The image state structure will be stored in this base.
maindrawbase = WIDGET_BASE( mainbase, EVENT_FUNC='cw_draw_eve', /ROW )
state.drawbase = maindrawbase

;Compute the full draw window parameters.
fullstate = { name:'full', id:0L, dx:0, dy:0, spx:0, spy:0,  xcen:0, ycen:0, $
              xoff:0, yoff:0, xsize:xsize, ysize:ysize, $
              xvisible:fvisible, yvisible:fvisible, zfact:1.0 }

sf = MAX( [xsize/fvisible, ysize/fvisible] ) + 1
nx = xsize / sf
ny = ysize / sf

fullstate.dx = nx * sf
fullstate.dy = ny * sf
fullstate.zfact = 1.0 / sf

fullstate.xoff = ( fvisible - nx ) / 2
fullstate.yoff = ( fvisible - ny ) / 2

;Compute the work window parameters.
workstate = { name:'work', id:0L, dx:xsize, dy:ysize, spx:0, spy:0, $
              xcen:0, ycen:0, xoff:0, yoff:0, xsize:xsize, ysize:ysize, $
              xvisible:wxvisible, yvisible:wyvisible, zfact:1 }

zfact = MAX( [xsize/wxvisible, ysize/wyvisible] )

IF zfact LT 1 THEN BEGIN
   ;The image size is less than the default size of the viewport.
   ;Determine if the image should be expanded (not more than a factor of
   ;3, unless fixed by WZOOMFACT keyword).

   zfact = MIN( [wxvisible/xsize, wyvisible/ysize] )
   IF zfact GT 6 THEN zfact=6

   IF KEYWORD_SET( wzoomfact ) THEN BEGIN
      IF wzoomfact GT 0 THEN zfact=wzoomfact
   ENDIF

   IF zfact LT 1 THEN zfact=1

   workstate.xsize = zfact * xsize
   workstate.ysize = zfact * ysize

   workstate.xvisible = workstate.xsize
   workstate.yvisible = workstate.ysize
   workstate.zfact = zfact

ENDIF

;Compute the zoom draw window parameters.
zoomstate = { name:'zoom', id:0L, dx:0, dy:0, spx:0, spy:0,  xcen:0, ycen:0, $
              xoff:0, yoff:0, xsize:xsize, ysize:ysize, $
              xvisible:zvisible, yvisible:zvisible, zfact:workstate.zfact*2 }

zoomstate.xcen = xsize / 2
zoomstate.ycen = ysize / 2

retain = 1

; Define the full-view draw window.
wb1    = WIDGET_BASE( maindrawbase, /COLUMN )
state.full_view = WIDGET_DRAW( wb1, /BUTTON_EVENTS, $
                  RETAIN=retain, XSIZE=fvisible, YSIZE=fvisible )
draw_state.full_view = state.full_view
fullstate.id = state.full_view
WIDGET_CONTROL, state.full_view, SET_UVALUE=fullstate

;Define the zoom draw window.
state.zoom_view = WIDGET_DRAW( wb1, /BUTTON_EVENTS, $
                  /MOTION_EVENTS, RETAIN=retain, $
                  XSIZE=zvisible, YSIZE=zvisible )
draw_state.zoom_view = state.zoom_view
zoomstate.id = state.zoom_view
WIDGET_CONTROL, state.zoom_view, SET_UVALUE=zoomstate

state.zoomupid = WIDGET_BUTTON( wb1, VALUE='Zoom+' )
state.zoomdnid = WIDGET_BUTTON( wb1, VALUE='Zoom-' )
;
zoombase = WIDGET_BASE( wb1, /ROW )
zlabel   = WIDGET_LABEL( zoombase, VALUE='Zoom factor:' )
state.zoomvid = WIDGET_LABEL( zoombase, $
   VALUE=STRING( zoomstate.zfact, FORMAT='(I3)' ) )

dummy1 = WIDGET_LABEL( wb1, VALUE=' ' )
state.objectid = WIDGET_LABEL( wb1, VALUE='                    ', /ALIGN_LEFT )
state.filterid = WIDGET_LABEL( wb1, VALUE='                    ', /ALIGN_LEFT )
state.airmasid = WIDGET_LABEL( wb1, VALUE='                    ', /ALIGN_LEFT )
state.exptimid = WIDGET_LABEL( wb1, VALUE='                    ', /ALIGN_LEFT )
state.utdateid = WIDGET_LABEL( wb1, VALUE='                    ', /ALIGN_LEFT )
state.uttimeid = WIDGET_LABEL( wb1, VALUE='                    ', /ALIGN_LEFT )

; Define the work draw window.
state.scroll = (workstate.xsize GT workstate.xvisible) OR $
               (workstate.ysize GT workstate.yvisible)
draw_state.scroll = state.scroll

IF workstate.xvisible GT workstate.xsize THEN workstate.xvisible=workstate.xsize
IF workstate.yvisible GT workstate.ysize THEN workstate.yvisible=workstate.ysize

state.workbase  = WIDGET_BASE( maindrawbase, /COLUMN )
IF state.scroll THEN BEGIN
   state.work_view = WIDGET_DRAW( state.workbase, /BUTTON_EVENTS, $
      /MOTION_EVENTS, RETAIN=retain, $
      /SCROLL, $
      XSIZE=workstate.xsize, $
      YSIZE=workstate.ysize, $
      X_SCROLL_SIZE=workstate.xvisible, $
      Y_SCROLL_SIZE=workstate.yvisible )
ENDIF ELSE BEGIN
   state.work_view = WIDGET_DRAW( state.workbase, /BUTTON_EVENTS, $
      /MOTION_EVENTS, RETAIN=retain, $
      XSIZE=workstate.xsize, $
      YSIZE=workstate.ysize )
ENDELSE
draw_state.work_view = state.work_view
workstate.id = state.work_view
WIDGET_CONTROL, state.work_view, SET_UVALUE=workstate

; Label the work window with its zoom factor and image file name.
wb = WIDGET_BASE( state.workbase, /ROW )
w1 = WIDGET_LABEL( wb, VALUE='Zoom factor:' )
w1 = WIDGET_LABEL( wb, VALUE=STRING( workstate.zfact, FORMAT='(I3)' ) )

;Load photometry parameters from a file?
IF ph_parms.parmfile NE '' THEN BEGIN
   it_pplod, ph_parms
ENDIF

IF state.scroll THEN BEGIN
   xoff = MAX( [0, (xsize-workstate.xvisible)/2 ] )
   yoff = MAX( [0, (ysize-workstate.yvisible)/2 ] )
   WIDGET_CONTROL, state.work_view, SET_DRAW_VIEW=[xoff, yoff]
ENDIF

WIDGET_CONTROL, draw_state.phparmsbase, SET_UVALUE=ph_parms

;Put some pointers into a 'state' structure and store it in the Top Level
;Base of the Pixel Editor. This provides the external event handler for
;the Pixel Editor with information it needs to access some of cw_itool's
;control data.
WIDGET_CONTROL, state.pixedbase, SET_UVALUE={imparmsbase:state.imparmsbase, $
   work_view:state.work_view, zoom_view:state.zoom_view}, /NO_COPY

;Store the main state structure.
stash = WIDGET_INFO( mainbase, /CHILD )

draw_state.mainstash = stash
WIDGET_CONTROL, state.drawbase, SET_UVALUE=draw_state

WIDGET_CONTROL, stash, SET_UVALUE=state, /NO_COPY

RETURN, mainbase
END