Viewing contents of file '../idllib/idl_5.2/lib/annotate.pro'
; $Id: annotate.pro,v 1.19.6.2 1999/01/26 23:23:26 alan Exp $
;
; Copyright (c) 1993-1999, Research Systems, Inc.  All rights reserved.
;	Unauthorized reproduction prohibited.
;
; Parameter definitions:
; For all objects:
; 0	p0_x
; 1	p0_y
; 2	p1_x
; 3	p1_y
; 4	color index
; 5	thickness
; 6	linestyle
; 7	size
; 8	fill style (0=none, 1 = solid, 2 = lines
; 9	fill space
; 10	fill angle
; 11	npoints / nchars
; 12	alignment
; 13    font
; 14	orientation
; 15	head size
; 17    line/arrow/solid_arrow
; 18	eccen
; 19    object type
; 20    interpolation
;
; Modes:
;	0 Text
;	1 Lines/Arrows
;	2 Polylines/Polygons, Drag
;	3 Circles
;	4 Squares



pro xmgr_fake, top, window
; simulate the xmanager routine for a non-widgetized environment.
; deliver events from the ann widget as well as motion and button
; events from the selected idl window.

buttons = 0
ix0 = -1
iy0 = -1

while 1 do begin
    ev = widget_event(top, BAD_ID = bad, /NOWAIT)   ;Widget event?
    if bad ne 0L then return			;Gone? Done.
    if ev.id ne 0L then annotate_event, ev $	;Dispatch it.
    else begin				;Check for mouse
	if !d.window ne window then wset, window
	CURSOR, ix, iy, /DEV, /NOWAIT
        if (ix < iy) ge 0 then begin    ;In window?
	    j = !err			;New buttons
	    i = j xor buttons		;changed buttons
	    if i ne 0 then $		;Button change...
	        ANN_DRAW_EVENT, { ANN_FAKE_EVENT, $
		    x:ix, y:iy, press: i and j, release: i and buttons }, top $
	    else if ((ix ne ix0) or (iy ne iy0)) then $
	        ANN_DRAW_EVENT, { ANN_FAKE_EVENT, $
		    x:ix, y:iy, press: 0L, release: 0L }, top
	    ix0 = ix
	    iy0 = iy
	    buttons = j
            endif               ;ix, iy >= 0
    endelse                 ;no widget event
  endwhile
end



function b_button, a	;Return a 1 bit deep bitmap given a byte image.
			;width of image MUST be a multiple of 8.
s = size(a)
b = bytarr(s[1]/8, s[2])
if (s[1] and 7) ne 0 then $
	message, 'B_BUTTON: image width must be a multiple of 8.'
subs = lindgen(n_elements(b)) * 8
for ibit = 0,7 do b = b or byte(2^ibit) * (a[subs+ibit] ne 0b)
return, reverse(b,2)
end


FUNCTION ann_closest, xy, n, pos	;return index of closest point
d = (xy[0,0:n-1]-pos[0])^2 + (xy[1,0:n-1]-pos[1])^2
junk = min(d, i)
return, i
end

FUNCTION ann_closest_obj, st
WIDGET_CONTROL, st.objlist, GET_UVALUE=l  ;Object list
dmin = 1e6
pos = st.pos
psize = n_elements(st.p)			;Elements/object
nrows = n_elements(l)/psize
if n_elements(l) le 1 then return, 0
for i=0L, nrows-1 do begin  ;Each object
    p = l[*,i]			;Ith object
    k = i
    n = long(p[11])
    case p[19] of			;Which object?
0:  BEGIN			;A mess...
	x = p[2]		;Text width
	dx = cos(p[14]* !dtor) * x
	dy = sin(p[14]* !dtor) * x
        q = [p[0] + dx * (0.5-p[12]), p[1] + dy * (0.5-p[12])]
	i = i + (n + psize - 1)/psize	;Next object
    ENDCASE
1:  q = [p[0]+p[2], p[1]+p[3]]/2.   ;Lines
2:  BEGIN			;Polygon
	j = psize * (i+1)
	xy = reform(l[j: j+2*n-1], 2, n)  ;Extract points
	xmin=min(xy[0,*], max=xmax)
	ymin=min(xy[1,*], max=ymax)
	q = [xmin+xmax, ymin+ymax]/2.
	i = i + (2*n+psize-1)/psize	;Next element
    ENDCASE
3:  q = p[[2,3]]		;Circle
4:  q = [p[0]+p[2], p[1]+p[3]]/2.   ;box
else: q = pos + 10		;Unknown obj, dist = big
    ENDCASE
    d = total((pos - q)^2)	;Distance...
    if d lt dmin then begin dmin = d & imin = k & endif
ENDFOR

if dmin lt 0.04 then return, imin else return, -1
END				;Ann_closest_obj



PRO ANN_GET_NUM_EVENT, ev
on_ioerror, bad_again
widget_control, ev.id, get_value=v
widget_control, ev.top, get_uvalue=u
val = float(v[0])
if val lt u.minv or val gt u.maxv then goto, bad_again
WIDGET_CONTROL, u.id, SET_VALUE=v[0]	;Save the correct value
WIDGET_CONTROL, ev.top, /DESTROY	;Done
return
bad_again:  WIDGET_CONTROL, ev.id, SET_VALUE=''
return
END


pro CW_CONF_EVENT, ev	;On any event, save the value and kill it
WIDGET_CONTROL, ev.id, GET_UVALUE=i	;Button index
WIDGET_CONTROL, ev.top, GET_UVALUE=temp ;Temp storage location
WIDGET_CONTROL, temp, SET_UVALUE=i	;save choice
WIDGET_CONTROL, ev.top, /DESTROY	;Kill it
return
END


FUNCTION CW_CONFIRM, message, choices
; Make a Modal widget with the given message as text, and with buttons
;   corresponding to the choices.  Return the index of the button pressed.
;
    temp = WIDGET_BASE()
    a = WIDGET_BASE(title='Confirmation',/COLUMN, UVALUE=temp, $
                    GROUP_LEADER=temp, /MODAL)
    for i=0, N_ELEMENTS(message)-1 DO junk = widget_label(a, value=message[i])
    b = WIDGET_BASE(a, /ROW)
    for i=0,n_elements(choices)-1 do $
	junk = WIDGET_BUTTON(b, VALUE=choices[i], /NO_REL, UVALUE=i)
    WIDGET_CONTROL, a, /REALIZE
    XMANAGER, 'Confirmation', a, EVENT_HANDLER='CW_CONF_EVENT'
    WIDGET_CONTROL, temp, GET_UVALUE=u, /DESTROY
return, u
end


FUNCTION ann_get_num, str, id, minv, maxv
; Get & check  a numeric value from a text widget.  If illegal format,
; use a modal widget to get a correct value.
	
on_ioerror, bad			;1st level call
v = float(str[0])
if v lt minv or v gt maxv then goto, bad
return, v			;OK

bad:
temp = WIDGET_BASE()
t = widget_base(title='Invalid Number', /column, $
                GROUP_LEADER=temp, /MODAL)
WIDGET_CONTROL, t, set_uvalue = { id:id, minv: minv, maxv: maxv} ;Save params
b = WIDGET_LABEL(t, value= 'An invalid number was entered')
b = WIDGET_LABEL(t, value = 'Range = '+string(minv)+' to '+string(maxv))
b = WIDGET_LABEL(t, value= 'Please enter the correct value')
j = WIDGET_TEXT(t, /frame, xsize=10, ysize=1, /EDIT)
WIDGET_CONTROL, t, /REAL
xmanager, 'Invalid Number', t, EVENT_HANDLER = 'ann_get_num_event'
WIDGET_CONTROL, id, GET_VALUE=v	;The correct value
WIDGET_CONTROL, temp, /DESTROY
return, float(v[0])
end



PRO ann_reset_mode, st, mode, submode, NOCLEAN = noclean
;Clean out the current object, reset mode

if n_elements(mode) le 0 then mode = st.mode	;Save old mode
if n_elements(submode) le 0 then submode = 0
newmode = st.mode ne mode

if newmode then begin
    m = st.mode_bases
    WIDGET_CONTROL, m[n_elements(m)-1], MAP=0	;Options base
    WIDGET_CONTROL, m[st.mode], MAP=0   ;Unmap old
    WIDGET_CONTROL, st.mode_buttons[mode], SET_BUTTON=1
    if mode ge 2 then begin		;Set polyfill controls?
	p = st.p
	WIDGET_CONTROL, st.spline_id, SET_VALUE=p[20]
        WIDGET_CONTROL, st.poly_style[mode], SET_VALUE=p[8]
	WIDGET_CONTROL, st.poly_angle[mode], SET_VALUE=p[10]
	WIDGET_CONTROL, st.poly_spacing[mode], SET_VALUE=p[9]
	endif
    st.mode = mode
    WIDGET_CONTROL, m[mode], MAP=1
    wset, st.draw_win		;Redraw the view window
    device, copy = [0,0, !d.x_size, !d.y_size, 0, 0, st.backing[1]]
    endif

if keyword_set(noclean) eq 0 then begin	;Remove current object
    st.p[11] = 0
    endif

st.open = 0
st.minor_mode = submode
st.p[19] = st.mode
WIDGET_CONTROL, st.minor_mode_id, SET_VALUE=submode
end


PRO ann_add_object, st			;Add current object to list
wset, st.backing[1]			;Make it permanent
p = st.p
ann_draw_object, st, p			;Draw obj in backing store
wset, st.draw_win
device, copy = [0,0, !d.x_size, !d.y_size, 0, 0, st.backing[1]] ;To draw window

psize = n_elements(p)
if p[19] eq 0 then begin		;text?
	n = strlen(st.txt)		;# of chars, Save in an array
	if n eq 0 then return		;Nothing to save
	p[11] = n
	a = bytarr(psize, (n+psize-1)/psize) ; Addtl bytes
	a[0] = byte(st.txt)
	p = [[p],[float(a)]]
endif else if p[19] eq 2 then begin	;Polygon?
	n = long(p[11])*2		;# of pnts required
	a = fltarr(psize, (n+psize-1)/psize)  ;Array into which to insert
	WIDGET_CONTROL, st.xy, GET_UVALUE=xy, /NO_COPY
	a[0] = xy[0:n-1]		;Insert it
	p = [[p], [a]]
endif	
	
WIDGET_CONTROL, st.objlist, GET_UVALUE = l, /NO_COPY
if n_elements(l) gt 1 then p = [[l], [p]]  ;Add to end of list
WIDGET_CONTROL, st.objlist, SET_UVALUE = p, /NO_COPY  ;Add to list
ann_reset_mode, st		;Reset mode
end				;Ann_add_object



pro ann_set_controls, st, p	;Set controls to corresp to object
WIDGET_CONTROL, st.color_id, SET_VALUE=p[4]
WIDGET_CONTROL, st.thick_id, SET_VALUE=p[5]*10.
WIDGET_CONTROL, st.linestyle_id, SET_VALUE=p[6]

case p[19] of
0:	BEGIN		;Text
	WIDGET_CONTROL, st.txt_size_id, SET_VALUE=p[7]*10.
	WIDGET_CONTROL, st.txt_id, SET_VALUE=st.txt
	WIDGET_CONTROL, st.ori_id, SET_VALUE=(p[14] + 360) mod 360
	WIDGET_CONTROL, st.txt_ali_id, SET_VALUE=p[12]*2
	WIDGET_CONTROL, st.txt_font_id, SET_VALUE=p[13]
	ENDCASE
1:	BEGIN		;Line/arrow
	WIDGET_CONTROL, st.line_arrow_id, SET_VALUE=p[17]
	WIDGET_CONTROL, st.head_size_id, SET_VALUE=p[15]*10.
	ENDCASE
2:	BEGIN	
	WIDGET_CONTROL, st.spline_id, SET_VALUE=p[20]
do_poly: i = long(p[19])
        WIDGET_CONTROL, st.poly_style[i], SET_VALUE=p[8]
	WIDGET_CONTROL, st.poly_angle[i], SET_VALUE=p[10]
	WIDGET_CONTROL, st.poly_spacing[i], SET_VALUE=p[9]
	ENDCASE
3: 	BEGIN		;Circle
	WIDGET_CONTROL, st.ecc_id, SET_VALUE=(p[18]-1.)*10.
	goto, do_poly
	ENDCASE
4:	goto, do_poly	;Rectangle
ENDCASE
END




pro ann_refresh_list, st, FROM_SCRATCH=redraw, WINDOW=window, MONO = mono
; FROM_SCRATCH = redraw from backing store(0)
; WINDOW = destination window
; MONO = color index to gray scale translation table

if n_elements(mono) le 0 then mono = 0
if n_elements(window) eq 1 then WSET, window

if keyword_set(redraw) then $	;Redraw from scratch?
    device, copy = [0,0, !d.x_size, !d.y_size, 0, 0, st.backing[0]]

WIDGET_CONTROL, st.objlist, GET_UVALUE=l, /NO_COPY
if n_elements(l) le 1 then goto, done	;Nothing...
s = size(l)
psize = s[1]			;# of columns
n = n_elements(l) / psize	;# of rows
for i=0L, n-1 do begin		;Do each object....
    p = l[*,i]			;The object
    m = 0			;# of addtl elements
    k = (i+1) * psize		;Next row
    if p[19] eq 0 then begin	;Text
	m = long(p[11])
	ann_draw_object, { txt: string(byte(l[k:k+m-1]))} , p, MONO = mono
    endif else if p[19] eq 2 then begin  ;polygon?
	m = long(p[11])*2
	xy = reform(l[k:k+m-1], 2, m/2)
	ann_draw_object, st, p, xy=xy, MONO= mono
    endif else ann_draw_object, st, p, MONO= mono
    i = i + (m+psize-1)/psize	;Next row
    endfor
done:  WIDGET_CONTROL, st.objlist, SET_UVALUE=l, /NO_COPY
end



function ann_load_obj, st, obj	;Load nth object from list.  Remove
; the object from the list.  return 0 if error, 1 if ok.
; reload st with object.

WIDGET_CONTROL, st.objlist, GET_UVALUE = l, /NO_COPY
if n_elements(l) le 1 then begin
quit:   WIDGET_CONTROL, st.objlist, SET_UVALUE=l, /NO_COPY
   return, 0
   endif
psize = n_elements(st.p)
n = n_elements(l) / psize	;# of objects
if obj lt 0 or obj ge n then goto, quit  ;There?
p = l[*,obj]
k = (obj+1) * psize
m = 0
mode = fix(p[19])
if mode eq 0 then begin		;Text?
    m = long(p[11])
    st.txt = string(byte(l[k:k+m-1]))  ;fetch the text
endif else if mode eq 2 then begin	;Polygon?
    j = long(p[11])
    m = j*2				;# of elements
    xy = reform(l[k:k+m-1], 2, j)
    WIDGET_CONTROL, st.xy, SET_UVALUE=xy, /NO_COPY
endif else m = 0

st.p = p			;Get the object...

m = (m+psize-1)/psize		;# of addtl rows
; print, 'loaded',obj,', rows ',m,', n =',n
if n eq (m+1) then l = 0 $	;last object
else BEGIN			;Remove this object
    v = replicate(1,n)
    v[obj:obj+m] = 0		;Rows we remove
    l = l[*,where(v)]
ENDELSE

WIDGET_CONTROL, st.objlist, SET_UVALUE = l, /NO_COPY  ;Restore list
ann_refresh_list, st, window=st.backing[1], /FROM_SCRATCH
ann_set_controls, st, p		;Reset the controls
return, 1
end

	

pro ann_xfer_file, st, file, SAVE=save, LOAD=load
; Transfer an annotate load/save file.

WIDGET_CONTROL, st.objlist, GET_UVALUE=l, /NO_COPY	;Current object list
psize = n_elements(st.p)
n = n_elements(l) / psize
magic = '414e4e00'XL

if keyword_set(save) then begin	;Save??
    if n eq 0 then begin
	i = CW_CONFIRM('There is nothing to save.', 'OK')
	WIDGET_CONTROL, st.objlist, SET_UVALUE=l, /NO_COPY
	return
	endif

    openw, unit, /GET_LUN, file, /XDR  ;Make the file
    writeu, unit, magic, psize	;Magic number, # of columns
    p = float([ !d.x_size, !d.y_size, !d.x_ch_size, !d.y_ch_size ])  ;Params
    writeu, unit, p

    for i=0L, n-1 do begin		;Write each object
	p = l[*,i]			;The object
	m = 0				;# of addtl elements
	k = (i+1) * psize		;Next row
	writeu, unit, p			;The array
	if p[19] eq 0 then begin	;Text?
	    m = long(p[11])		;Strlen
	    writeu, unit, byte(l[k:k+m-1])  ;The characters
	endif else if p[19] eq 2 then begin  ;Polygon?
	    m = long(p[11])*2
	    writeu, unit, l[k:k+m-1]	;The points
	endif
	i = i + (m+psize-1)/psize
	endfor
    writeu, unit, replicate(-1.0, psize)
    free_lun, unit
    WIDGET_CONTROL, st.objlist, SET_UVALUE=l, /NO_COPY  ;Restore

endif else begin				;LOAD
    openr,unit, /GET_LUN, file, /XDR, ERROR=i  ;Try to open it...
    if i lt 0 then begin		;No file..
	i = CW_CONFIRM(['File not found or unreadable', file], 'OK')
	return
	endif
    i = 0L			;Read magic
    columns = 0L
    readu, unit, i, columns
    if i ne magic then begin
	i = CW_CONFIRM(['File is not an annotate data file.', file], 'OK')
	return
	endif
    params = fltarr(4)
    readu, unit, params

    while 1 do begin		;Read each record
	p = fltarr(columns)
	readu, unit, p
;	print, p[19]
	if p[19] eq -1 then begin	;Done....
	    free_lun, unit
	    WIDGET_CONTROL, st.objlist, SET_UVALUE = l, /NO_COPY ;save list
	    ann_refresh_list, st, WINDOW=st.backing[1], /FROM_SCRATCH
	    wset, st.draw_win
	    device, copy = [0,0, !d.x_size, !d.y_size, 0, 0, st.backing[1]]
	    return
	    endif
	m = 0L
	if p[19] eq 0 then begin	;string
	    m = long(p[11])		;# of addtl elements
	    a = bytarr(m)
	    readu, unit, a
	    p = [[p], [fltarr(psize, (m+psize-1)/psize)]]  ;what we add
	    p[psize] = a		;Insert chars....
	endif else if p[19] eq 2 then begin	;polygon
	    m = long(p[11])*2		;# of addtl elements
	    a = fltarr(m)
	    readu, unit, a
	    p = [[p], [fltarr(psize, (m+psize-1)/psize)]]  ;what we add
	    p[psize] = a
	endif
;		Adjust size for possible expansion....
	if columns lt psize then p = [p, fltarr(columns-psize)] $
	else if psize lt columns then p = p[0:psize-1]

	if n_elements(l) gt 1 then l = [[temporary(l)], [p]] $
	else l = p
	endwhile
endelse
end

pro annotate_ps, st, draw_ps, include_image

if (LMGR(/DEMO)) then begin
   tmp = DIALOG_MESSAGE( /ERROR, $
         'Postscript Output: Feature disabled for demo mode.')
   return
endif

tvlct, r,g,b, /get              ;current color table
old = !d.name			;Current device name

file = PICKFILE(FILE='annotate.ps', /WRITE, FILTER='*.ps', /NOCONF)

if include_image then begin
    if draw_ps then WSET, st.backing[0] $ ;Original image
    else WSET, st.backing[1]	;image + our annotations
    a = tvrd()
    endif

width = st.ps_width		;Output width
ch = [!d.x_ch_size, !d.y_ch_size] / float([!d.x_size, !d.y_size])
if st.ps_units then width = width / 2.54	;From cm to inches
height = width * !d.y_size / !d.x_size	;Height	

print, 'Color=',st.ps_color
SET_PLOT,'PS'
DEVICE, file=file, COLOR=st.ps_color
if st.ps_orien then DEVICE, /LANDSCAPE $
else DEVICE, /PORTRAIT
DEVICE, XSIZE = width, YSIZE=height, /INCHES	;Set size
tvlct,r,g,b
if st.ps_color eq 0 then $	;To BW from RGB
	l = bytscl(r * .3 + .6 * g + .11 * b)

if include_image then begin	;Include image with new file?
    if st.ps_color eq 0 then a = l[a]
    tv, a                   ;Output image
    endif

if draw_ps then begin
    device, set_char = ch * !d.x_size  ;Proportional char size (aspect = same)
    ann_refresh_list, st, MONO = l
    endif

if include_image then device, /close
set_plot, old               ;done
end


pro annotate_event, ev

WIDGET_CONTROL, ev.top, GET_UVALUE = st, /NO_COPY   ;Our data structure
WIDGET_CONTROL, ev.id, GET_UVALUE = u		;The object uvalue

ldr = strmid(u, 0, 1)
IF ldr eq 'M' THEN BEGIN		;New mode?
    ann_reset_mode, st, fix(strmid(u,1,2)), 0
    goto, done
    ENDIF

IF ldr eq '#' THEN BEGIN
    WIDGET_CONTROL, ev.id, GET_VALUE = v
    ldr = '@'
ENDIF

IF ldr eq '@' THEN BEGIN		;Execute string?
	i = EXECUTE(strmid(u,2,100))	;Execute the string....
	IF strmid(u, 1, 1) eq 'R' THEN BEGIN ;Redraw?
redraw:   if st.open then begin
	    wset, st.draw_win
	    handle = st.minor_mode eq 1
	    p = st.p
	    ANN_DRAW_OBJECT, st, p, handle, REFRESH=st.backing[1]
	    st.p = p
	    st.handle = handle
	    ENDIF
	ENDIF		;Redraw
ENDIF ELSE IF u eq 'Help' THEN BEGIN	;ldr eq '@'
	xdisplayfile, filepath("annotate.txt", subdir=['help', 'widget']), $
;	xdisplayfile, 'annotate.txt', $
		title = "Annotate Help", $
		group = ev.top, $
		width = 72, height = 24
ENDIF else if u eq 'Options' THEN BEGIN
	m = st.mode_bases
	WIDGET_CONTROL, m[st.mode], MAP=0
	WIDGET_CONTROL, m[n_elements(m)-1], /MAP
ENDIF else if u eq 'Dismiss' THEN BEGIN
	m = st.mode_bases
	WIDGET_CONTROL, m[n_elements(m)-1], MAP=0
	WIDGET_CONTROL, m[st.mode], MAP=1
ENDIF ELSE IF u eq 'TOPROW' THEN BEGIN
    WIDGET_CONTROL, ev.top, /HOURGLASS
    isDemo = LMGR(/DEMO)
    case ev.value of
   'Exit' : BEGIN
	    for i=0,1 do wdelete, st.backing[i]
	    widget_control, ev.top, /DESTROY
	    return
	    ENDCASE
    'Save' : BEGIN
    save_file:	   if st.open then ann_add_object, st	;Add current object?
           if (isDemo) then begin
               tmp = DIALOG_MESSAGE( /ERROR, $
                     'Save: Feature disabled for demo mode.')
           endif else ann_xfer_file, st, st.file, /SAVE
	   ENDCASE
    'Save As': BEGIN
        if (isDemo) then begin
           tmp = DIALOG_MESSAGE( /ERROR, $
                 'Save As: Feature disabled for demo mode.')
        endif else BEGIN
	   st.file = PICKFILE(FILE=st.file, /WRITE)
	   goto, save_file
        endelse
	ENDCASE
    'Load': BEGIN
	st.file = PICKFILE(FILE=st.file, /READ, /MUST_EXIST, FILTER='*.dat')
	ann_xfer_file, st, st.file, /LOAD
	ENDCASE
    'TIFF': BEGIN
        if (isDemo) then begin
           tmp = DIALOG_MESSAGE( /ERROR, $
                 'WRITE_TIFF: Feature disabled for demo mode.')
        endif else BEGIN
	   file = PICKFILE(FILE='annotate.tif', /WRITE, FILTER='*.tif', /NOCONF)
	   WSET, st.backing[1]
	   tvlct, red, green, blue, /GET
	   WRITE_TIFF, file, tvrd(/ORDER), 1, RED=red, GREEN=green, BLUE=blue
        endelse
	ENDCASE
    'GIF': BEGIN
        if (isDemo) then begin
           tmp = DIALOG_MESSAGE( /ERROR, $
                 'WRITE_GIF: Feature disabled for demo mode.')
        endif else BEGIN
	   file = PICKFILE(FILE='annotate.gif', /WRITE, FILTER='*.gif', /NOCONF)
	   WSET, st.backing[1]
	   WRITE_GIF, file, tvrd(ORDER=0)
        endelse
	ENDCASE
    ; If demo mode, disable ps in annotate_ps
    'PostScript': annotate_ps, st, 0, 1		;Postscript bitmap
    'Everything': annotate_ps, st, 1, 1		;do it with PS commands
    'Objects only': annotate_ps, st, 1, 0
    'Clear': BEGIN
	    WIDGET_CONTROL, st.objlist, SET_UVALUE=0  ;Clean up object list
	    wset, st.backing[1]		;redraw all objects except this one
	    device, copy = [0,0, !d.x_size, !d.y_size, 0, 0, st.backing[0]]
	    wset, st.draw_win
	    device, copy = [0,0, !d.x_size, !d.y_size, 0, 0, st.backing[0]]
	    ann_reset_mode, st, st.mode, 0
	ENDCASE
    ENDCASE
ENDIF ELSE IF u eq 'Save' THEN BEGIN
    if st.open THEN ann_add_object, st
ENDIF ELSE help, /st, u, ev


done: WIDGET_CONTROL, ev.top, SET_UVALUE = st, /NO_COPY
end


PRO ANN_MOVE_RESIZE, st, mode, first, last

if st.open eq 0 then return
handle = st.handle
pos = st.pos
p = st.p
if first then begin		;Initial hit?
    np = ([2,2,4,4,4])[mode]	;# of points
		;distances from handle points and midpoint
    h = handle[*,0:np-1]
    d = [[h], [total(h,2)/np]] - (pos # replicate(1.,np+1))
    d = min(total(d^2, 1), i)	;Dist squared, get closest
    if i eq np then i = 5	;always use 5 for last point
    if d lt 0.01 then begin	;Close enough?
	st.ihandle = i
	if mode eq 0 then begin	;Text mode?
	    if p[7] eq 0.0 then p[7] = 1.0	;Size
	    st.temp[0] = p[7] / p[2]		;1./(Length of siz=1)
	 endif else if mode eq 3 then begin	;Circle?  Save params.
	    c = p[2:3]
	    st.temp = [p[0:1] - c, total((pos - c)^2)]  ;dx,dy, len
	endif
        if i ne 5 then st.orig = handle[*,np-1-i] $  ;Anchor point
	else st.orig = pos
    endif else st.ihandle = -1
endif					;First

ihandle = st.ihandle
if ihandle lt 0 then return		;A hit?
d = pos - st.orig		;Movement
; if d(0) eq 0.0 and d(1) eq 0.0 and first eq 0 then goto, no_move

if ihandle eq 5 then begin		;Move?
    st.orig = pos		;Reset it
    if mode eq 2 then begin	;Polygons?
	if p[11] gt 1 THEN BEGIN   ;Polygons
	    WIDGET_CONTROL, st.xy, GET_UVALUE=xy, /NO_COPY
	    xy = xy + (d # replicate(1, p[11]))  ;Translate
	    WIDGET_CONTROL, st.xy, SET_UVALUE=xy, /NO_COPY
	    ENDIF
    endif else p[0] = p[0:3] + [d,d]  ;Just move origin
ENDIF ELSE BEGIN			;Sizing handle
    if (mode gt 1) then begin		;Need scale factor?
	s = handle[*,ihandle] - st.orig	;stretch factor
	if min(abs(s)) lt 0.001 then goto, no_move
	s = (pos - st.orig) / s	  ;Scale factor
	if min(abs(s)) lt 0.001 then goto, no_move
    endif
    case mode of
0:	BEGIN			;Text
	r = sqrt(total(d^2))		;Length of line
	if r lt 0.001 then goto, no_move
	p[14] = atan(d[1], d[0]) * !radeg	;New orientation
	if ihandle eq 0 then p[14] = p[14] + 180.
	p[7] = r * st.temp[0]		;New size
	k = long(3 * ihandle + 2 * p[12]) ;Subs from 0 to 5.
	p[0] = pos + ([0, -0.5, -1., -1, -0.5, 0])[k] * d
	ENDCASE
1:	p[ihandle * 2] = pos		;Line, just move vertex
2:	if p[11] gt 1 THEN BEGIN	;Polygon, Scale all verts
	    WIDGET_CONTROL, st.xy, GET_UVALUE=xy, /NO_COPY
	    xy0 = st.orig # replicate(1.0,p[11])
	    xy = (xy - xy0) * (s # replicate(1.0,p[11])) + xy0
	    WIDGET_CONTROL, st.xy, SET_UVALUE=xy, /NO_COPY
	    ENDIF
3:	BEGIN			;Circle
	    c = p[2:3]		;Center
	    r = sqrt(total((pos - c)^2)/st.temp[2])  ;Dist from ctr
	    p[0] = st.temp[0:1] * r + c
	ENDCASE
4:	BEGIN			;Rectangle
	    x0 = [st.orig, st.orig]
	    p[0] = (p[0:3] - x0) * s[[0,1,0,1]] + x0
	ENDCASE
    ENDCASE
ENDELSE

no_move:
    ANN_DRAW_OBJECT, st, p, handle, REFRESH=st.backing[1]  ;Refresh
    st.p = p
    st.handle = handle
END			;ANN_MOVE_RESIZE




PRO ANN_DRAW_EVENT, ev, ann_base	;Handle events from drawable

if n_elements(ann_base) eq 0 then $
	WIDGET_CONTROL, ev.id, GET_UVALUE = ann_base
WIDGET_CONTROL, ann_base, GET_UVALUE=st, /NO_COPY
mode = st.mode

WSET, st.draw_win
if ev.press eq 4 then begin	;Right button to close object
	if st.open then ann_add_object, st
	goto, skip_it
endif else if ev.press eq 2 then begin
	if st.open eq 0 then goto, skip_it
	i = st.minor_mode eq 0	;Switch modes
        st.minor_mode = i	;Reset minor mode
	WIDGET_CONTROL, st.minor_mode_id, SET_VALUE=i
	p = st.p
	ANN_DRAW_OBJECT, st, p, i, REFRESH=st.backing[1]
	st.handle = i
	goto, skip_it
endif

pos = [ev.x, ev.y]

first = 0
drag = 0
last = 0
if st.buttons eq 0 then begin	;First time?
  if ev.press ne 1 then begin	;Move?
	if !x.s[1] ne 0 then pos = convert_coord(pos, /DEVICE, /TO_DATA)
	WIDGET_CONTROL, st.instruct, SET_VALUE=string(pos[0:1])
	goto, skip_it
  endif else begin
	first = 1
	WIDGET_CONTROL, st.instruct, SET_VALUE='Right button to close'
	st.buttons = 1
  endelse
endif else begin		;Not first
  if ev.release ne 1 then drag = 1 $
  else begin
	last = 1
	st.buttons = 0
  endelse
endelse


g = st.granularity
pos = round(pos/g)*g / [!d.x_size, !d.y_size]
if drag and (st.pos[0] eq pos[0]) and (st.pos[1] eq pos[1]) then goto, skip_it
st.pos = pos


if st.minor_mode eq 1 then begin	;MOVE/RESIZE
    ANN_MOVE_RESIZE, st, mode, first, last
    goto, skip_it
ENDIF ELSE if st.minor_mode eq 2 then begin	;Select
    if first then begin
	ann_reset_mode, st, st.mode, 2
	k = ann_closest_obj(st)
	if k ge 0 then begin		;Found one?
	   st.temp[0] = k
	   if ann_load_obj(st, k) then begin	;Find it?
		p = st.p
		st.minor_mode = 1	;Now in move/resize
		ann_reset_mode, st, p[19], 1, /NOCLEAN
		st.open = 1
		h = 1
		wset, st.draw_win
		ANN_DRAW_OBJECT, st, p, h, REFRESH=st.backing[1]
		st.handle = h
		ANN_MOVE_RESIZE, st, st.mode, first, last
		endif
	endif
    endif
    goto, skip_it
ENDIF			;Minor = 2

p = st.p

if st.open eq 0 then begin	;First time
	p[11] = 0
	WIDGET_CONTROL, st.fill_mode_id, SET_VALUE=0
	WIDGET_CONTROL, st.minor_mode_id, SET_VALUE=0
	st.fill_mode = 0
	st.minor_mode = 0
	ENDIF
st.open = 1

case mode of
0: BEGIN			;Text
    IF first THEN BEGIN
	WIDGET_CONTROL, st.txt_id, GET_VALUE=h
	st.txt = h[0]
	ENDIF
no_name_label:
    if first then p[2] = pos
    p[0] = pos
    h = last
    ANN_DRAW_OBJECT, st, p, h, REFRESH=st.backing[1]
    if last then begin
	st.handle = h
	st.minor_mode = 1		;Go to move resize mode
	WIDGET_CONTROL, st.minor_mode_id, SET_VALUE=1
	endif
   ENDCASE

1: goto, no_name_label		;Dragging line or arrow


2:  BEGIN			;Polygon/polyline
    n = long(p[11])
    if n eq 0L THEN BEGIN	;Initialize line buffer?
	xy = FLTARR(2,256)
	ENDIF ELSE WIDGET_CONTROL, st.xy, GET_UVALUE=xy, /NO_COPY
   case st.fill_mode of		;What are we doing?
   0: BEGIN		;Vector /drag mode
	if n_elements(xy) le n*2 then xy = [[xy], [FLTARR(2,n)]]  ;Extend it
	addok = 1
	; avoid adding the same point repeatedly (which crashes the spline .pro)
        IF (n GE 1L) THEN BEGIN
	  IF ((xy[0,n-1] EQ st.pos[0]) $
		AND (xy[1,n-1] EQ st.pos[1])) THEN addok = 0
	ENDIF
	IF (addok) THEN BEGIN
	  xy[0,n] = st.pos			;Last point
	  if st.buttons then p[11] = n+1 	;Dragging
	ENDIF
      ENDCASE		;Vector	
   1: BEGIN		;Edit mode
	if n eq 0 then goto, skip_it1
	if first then begin
		if n_elements(xy) le n*2 then xy = [[xy], [FLTARR(2,n)]]
		j = ann_closest(xy, n, pos)
		xy[0,n] = j
	endif else j = xy[0,n]
	xy[0,j] = pos		
      ENDCASE
   2: BEGIN		;Delete
	if (n le 0) or (last eq 0) then goto, skip_it1
	j = ann_closest(xy, n, pos)	;Point to delete
	p[11] = n-1
	if n eq 1 then goto, skip_it
	if j eq 0 then xy = xy[*,1:*] $
	else if j eq n-1 then xy = xy[*, 0:n-2] $
	else xy = [[xy[*,0:j-1]], [xy[*, j+1:*]]]
      ENDCASE		;delete
  ENDCASE		;st.fill_mode
  WIDGET_CONTROL, st.xy, SET_UVALUE=xy, /NO_COPY
  ANN_DRAW_OBJECT, st, p, REFRESH=st.backing[1]
 ENDCASE		;2

3: goto, no_name_label		;Circle


4: goto, no_name_label		;Box
ENDCASE

skip_it:
    if n_elements(p) gt 1 then st.p = p
    WIDGET_CONTROL, ann_base, SET_UVALUE=st, /NO_COPY
    return
skip_it1:
    WIDGET_CONTROL, st.xy, SET_UVALUE=xy, /NO_COPY
    goto, skip_it
end



pro ANN_DRAW_OBJECT, st, p, handle, REFRESH = refresh, XY=xy, MONO = mono

c = long(p[4])			;Color index
if keyword_set(mono) then c = mono[c]	;Outputting Postscript for BW?

if n_elements(refresh) gt 0 then $  ;Refresh?
	device, copy = [0,0, !d.x_size, !d.y_size, 0, 0, refresh]

case p[19] of		;What type of object
0: BEGIN		;Text
	str = '!' + strtrim(fix(p[13])>3,2) + st.txt + '!3'
	xyouts, p[0], p[1], /NORM, str, $
		COLOR = c, CHARSIZE = p[7], $
		ALI = p[12], ORI=p[14], CHARTHICK = p[5], WIDTH=x
	p[2] = x 		;Save width
	if keyword_set(handle) then begin
	    dx = cos(p[14]* !dtor) * x
	    dy = sin(p[14]* !dtor) * x
	    handle = [[ p[0]-p[12] * dx, p[1]-p[12] * dy], $
		[p[0] + (1.-p[12]) * dx, p[1] + (1.-p[12]) * dy]]
	    n = 2
do_handle:  PLOTS, handle, /NORM, PSYM=6
	    PLOTS, total(handle,2)/n, /NORM, PSYM=6  ;The center
	    ENDIF
   ENDCASE

1: BEGIN
    if p[17] ne 0 THEN BEGIN	;Lines/Arrows
	if p[15] le 0.0 then p[15] = 1.0
	ARROW, p[2], p[3], p[0], p[1], /NORM, $
		COLOR = c, THICK = p[5], SOLID=p[17] eq 2, $
		HSIZE = p[15] * !D.X_SIZE/20.
    ENDIF ELSE PLOTS, p[[2,0]], p[[3,1]], /NORM, COLOR=c, THICK=p[5], $
		LINESTYLE=p[6]
    if keyword_set(handle) then begin
	n = 2
	handle = [[p[0], p[1]], [p[2], p[3]]]
	goto, do_handle
	ENDIF
   ENDCASE

2: BEGIN		;Polygon
	n = p[11]
do_fill:
	if n gt 1 THEN BEGIN
	   if n_elements(xy) le 1 then WIDGET_CONTROL, st.xy, GET_UVALUE = xy
	   if (p[19] eq 2) and (p[20] eq 1) and (n gt 2) then $  ;Splines?
	       spline_p, reform(xy[0,0:n-1]), reform(xy[1,0:n-1]), $
			x, y, INTERV= 0.01 $
	   else begin			;Not splines
		x = xy[0,0:n-1]
		y = xy[1,0:n-1]
	   endelse
	   if (p[8] ne 1) or (n eq 2) THEN BEGIN   ;Outline it?
		plots, x, y, /NORM, COLOR = c, $  ;Draw outline
		    THICK = p[5], LINESTYLE = p[6]
		if p[8] eq 2 THEN BEGIN  ;line fill?
		   plots, x[[n-1,0]], y[[n-1,0]], /NORM, COLOR = c, $ ;Close it
		    THICK = p[5], LINESTYLE = p[6]
		   if n ge 3 THEN POLYFILL, x, y, /NORM, COLOR = c, $
			THICK = p[5], ORI = p[10], SPACING = p[9]/100.
		ENDIF
	  ENDIF ELSE IF N GE 3 THEN $		;Solid fill
		POLYFILL, x,y, /NORM, COLOR = c
	ENDIF ELSE IF (n EQ 1) THEN BEGIN
	   ; If only one point, we may still need handles...
	   if n_elements(xy) le 1 then WIDGET_CONTROL, st.xy, GET_UVALUE = xy
	   x = xy[0,0]
	   y = xy[1,0]
	ENDIF
	; get the handle bounds...
	IF (keyword_set(handle) AND (n GE 1)) THEN BEGIN ;Draw resizing handles
	  xmin=min(x, max=xmax)
	  ymin=min(y, max=ymax)
	  n = 4
	  handle = [[xmin, ymin], [xmin, ymax], [xmax,ymin],[xmax, ymax]]
	  GOTO, do_handle
	ENDIF
   ENDCASE
3: BEGIN		;Circle/ Ellipse
	a = !d.x_size / float(!d.y_size)	;Aspect ratio
	dx = p[0] - p[2]
	dy = p[1] - p[3]
	r = sqrt(dx^2 + dy^2)
	if r eq 0.0 then return
	dx = dx/r
	dy = dy/r
	n = 128
	t = findgen(n) * (( 2 * !pi)/ (n-1))	;Angles..
;			The whole ball of wax
	xy = [[cos(t) * r], [sin(t) * r/p[18]]] # [[dx, -dy],[dy, dx]]
	xy = reform([p[2] + xy[*,0], p[3] + a*xy[*,1]], n,2,/OVER)
	xy = transpose(xy)
	goto, do_fill
   ENDCASE	;Circle
4: BEGIN	;Box
	xy = p[[[0,1],[0,3],[2,3],[2,1],[0,1]]]
	n = 5
	goto, do_fill
   ENDCASE
else: print,'Unknown object'
ENDCASE

end



pro ann_make_draw_button, st
; Make mode buttons
a = widget_base(st.base, /ROW, Exclusive=!version.os ne 'Win32')
id = lonarr(n_elements(st.mode_buttons))	;Id arrays

wsize = 32
window, xsize=wsize, ysize=wsize, /pix, /free
w2 = wsize/2			;Handy constants
w9 = 9*wsize/10
w1 = wsize/10
w3 = wsize/3

xyouts, w1, wsize/4, 'Abc', /dev, chars= wsize/(3.2* !d.x_ch_size)	;Text
id[0] = WIDGET_BUTTON(a, /NO_REL, value= b_button(tvrd()))
erase

arrow, w1, w3, w9, w3, /device, hsize = 10	;Arrows/ lines
plots, [w1, w9], wsize - [w3, w3], lines=3, /DEV
id[1] = WIDGET_BUTTON(a, /NO_REL, value= b_button(tvrd()))
erase

polyfill, [w1,w2,w1], [w1, w9, w9], /dev	;Polygons
plots, [ 2, 4, 6, 8, 10, 12, 10, 8, 8]*wsize/16, $	;Drag/Draw
       [ 1, 3, 7, 9, 9,  11, 16, 14, 6]*wsize/20, /dev
id[2] = WIDGET_BUTTON(a, /NO_REL, value= b_button(tvrd()))
erase


n = 32				;Circles
t = findgen(n) *(2 * !pi / (n-1))
plots, wsize/3 * cos(t) + (w2), wsize/3 * sin(t) + w2, /dev
id[3] = WIDGET_BUTTON(a, /NO_REL, value= b_button(tvrd()))
erase

plots, [w1,w1, w9, w9, w1], [w1, w9, w9, w1, w1], /dev   ;Squares
id[4] = WIDGET_BUTTON(a, /NO_REL, value= b_button(tvrd()))
erase

for i=0, n_elements(id)-1 do $		;Set up uv = Mn
	WIDGET_CONTROL, id[i], SET_UVALUE='M'+strtrim(i,2)
WIDGET_CONTROL, id[0], /SET_BUTTON	;Set initial choice
wdelete
st.mode_buttons = id
end





PRO ANNOTATE, DRAWABLE = draw, WINDOW = window, LOAD_FILE=load_file, $
	COLOR_INDICES=color_indices, TEK_COLORS = tek_colors

;+
; NAME:
;	ANNOTATE
;
; PURPOSE:
;	This procedure is a general purpose drawing program/widget to
;	annotate displays. Drawing objects include text, lines, arrows,
;	polygons, rectangles, circles, and ellipses.
;
; CATEGORY:
;	Widgets.  Will also work with plain windows.
;
; CALLING SEQUENCE:
;	ANNOTATE
;
; INPUTS:
;	No required inputs.
;
; KEYWORD PARAMETERS:
;	COLOR_INDICES:	an array of color indices from which the user
;			can choose colors. For example, to allow the user
;			to choose 10 colors, spread evenly over the
;			available indices, set the keyword as folows:
;			COLOR_INDICES = INDGEN(10) * (!D.N_COLORS-1) / 9
;	DRAWABLE:	the widget ID of the draw widget for the annotations.
;			This is mutually exclusive with WINDOW.
;	LOAD_FILE:	the name of an annotation format file to load after
;			initialization.
;	TEK_COLORS:	if set, the Tektronix color table is loaded
;			starting at color index TEK_COLORS(0), with
;			TEK_COLORS(1) color indices. The Tektronix color
;			table contains up to 32 distinct colors suitable
;			for graphics.
;	WINDOW:		the window index number of the window to receive the
;			annotations.
;
; OUTPUTS:
;	This procedure has no explicit outputs. Menu choices exist to
;	write TIFF, GIF, or PostScript bitmap files. Encapsulated
;	or standalone PostScript files may also be created.
;
; SIDE EFFECTS:
;	Annotations are made in the designated window or draw widget.
;
; RESTRICTIONS:
;	This is a simple drawing program.
;
; PROCEDURE:
;	If neither TEK_COLORS or COLOR_INDICES are specified, the default
;	is to load 10 colors, evenly distributed over those available.
;
;	If neither WINDOW or DRAWABLE are specified, the current window
;	is used.
;
; EXAMPLE:
;	TVSCL, HANNING(300,200)	;Output an image in the current window
;	ANNOTATE		;Annotate it
;
; MODIFICATION HISTORY:
;	DMS, RSI, July, 1993.  Original version.
;-



nmodes = 5
sl_width = 128

st = { ANN_STATE, $
    mode : 0, $
    minor_mode_id: 0L, $
    minor_mode: 0, $
    p: fltarr(21), $		;General params
    open : 0, $			;NE 0 if an object is open
    base : 0L, $
    current: 0L, $		;Current object if saved on list
    objlist : 0L, $		;object container
    xy: 0L, $
    pos:  fltarr(2), $		;Current position
    orig: fltarr(2), $		;Beginning of drag
    txt: 'Text',$		;Current contents of text widget
    mode_buttons: lonarr(nmodes), $
    mode_bases : lonarr(nmodes+1), $
    draw_win : 0L, $
    backing : lonarr(2), $	;0 = original bitmap, 1 = with closed objects
    color_id: 0L, $
    thick_id: 0L, $
    txt_id: 0L, $
    txt_size_id: 0L, $
    txt_ali_id: 0L, $
    txt_font_id: 0L, $
    ori_id: 0L, $
    linestyle_id : 0L, $
    buttons: 0L, $
    granularity : 1.0, $
    instruct: 0L, $
    line_arrow_id: 0L, $
    head_size_id: 0L, $
    fill_mode_id: 0L, $
    fill_mode: 0, $
    handle: fltarr(2,4), $
    temp: fltarr(3), $
    poly_style: lonarr(nmodes), $
    poly_angle: lonarr(nmodes), $
    poly_spacing: lonarr(nmodes), $
    ecc_id: 0L, $
    spline_id: 0L, $
    file: 'annotate.dat', $
    ihandle: 0, $
    ps_encap: 0, $
    ps_color: 0, $
    ps_orien: 0, $
    ps_width: 5.0, $
    ps_units: 0 }

st.p[18] = 1.0	;Eccent

ann_base = WIDGET_BASE(title='Annotate', /COLUMN)	;Our base
st.base = ann_base

win = !d.window		;Default window
use_xmgr = 0
if n_elements(window) eq 1 then win = window $
else if n_elements(draw) eq 1 then begin
	widget_control, draw, get_value = win, EVENT_PRO = 'ANN_DRAW_EVENT', $
	    SET_UVALUE = ann_base
	use_xmgr = 1
	endif
if win lt 0 then message,'No draw window active'
st.draw_win = win


wset, win
nx = !d.x_size
ny = !d.y_size

for i=0,1 do begin		;Get 2 backing pixmaps.
    window, /FREE, /PIXMAP, xs = nx, ys = ny
    device, copy = [0,0,nx, ny, 0, 0, win]  ;Copy original window
    st.backing[i] = !d.window
    endfor


st.objlist = WIDGET_BASE(ann_base, UVALUE = 0, $	;Object list holder
    xsize = 2, ysize=2)
st.xy = WIDGET_BASE(ann_base, UVALUE = 0, $	;Polygon points holder
    xsize = 2, ysize=2)

junk = WIDGET_BASE(ann_base, /ROW)
tmp = CW_PDMENU(junk, /RETURN_NAME, UVALUE='TOPROW', [ $
	{CW_PDMENU_S, flags: 1, name: 'File'}, $
	{CW_PDMENU_S, flags: 0, name: 'Load'}, $
	{CW_PDMENU_S, flags: 0, name: 'Save'}, $
	{CW_PDMENU_S, flags: 0, name: 'Save As'}, $
	{CW_PDMENU_S, flags: 1, name: 'Write PostScript'}, $
	{CW_PDMENU_S, flags: 0, name: 'Everything'}, $
	{CW_PDMENU_S, flags: 2, name: 'Objects only'}, $
	{CW_PDMENU_S, flags: 1, name: 'Export Bitmap'}, $
	{CW_PDMENU_S, flags: 0, name: 'GIF'}, $
	{CW_PDMENU_S, flags: 0, name: 'PostScript'}, $
	{CW_PDMENU_S, flags: 2, name: 'TIFF'}, $
	{CW_PDMENU_S, flags: 0, name: 'Clear'}, $
	{CW_PDMENU_S, flags: 2, name: 'Exit'}])

tmp = WIDGET_BUTTON(junk, value='Help', UVALUE='Help', /NO_REL)
tmp = WIDGET_BUTTON(junk, value='Options', UVALUE='Options', /NO_REL)

junk = WIDGET_BASE(ann_base, /ROW)
tmp = WIDGET_BUTTON(junk, VALUE='Save', UVALUE='Save', /NO_REL)
st.minor_mode_id = CW_BGROUP(junk, /EXCLUSIVE, /ROW, /NO_REL, $
	LABEL_LEFT = 'Mode:', $
	['Draw', 'Edit','Select'], $
        UVALUE='@Rst.minor_mode = ev.value', SET_VALUE=0)

ann_make_draw_button, st

scolor = 0
ncolors = 10
if n_elements(tek_colors) ge 1 then begin
    scolor = tek_colors[0]
    if n_elements(tek_colors) ge 2 then ncolors = tek_colors[1] else ncolors=8
    color_indices = indgen(ncolors) + scolor
endif else if n_elements(color_indices) gt 0 then begin
	ncolors = n_elements(color_indices)
endif else begin
	color_indices = (!d.n_colors-1) * lindgen(ncolors) / (ncolors-1)
endelse

st.color_id = CW_CLR_INDEX(ann_base, LABEL = 'Color:', XSIZE=160, $
	NCOLORS = ncolors, START_COLOR = scolor, $
	COLOR_VALUES=color_indices, uvalue = '@Rst.p[4] = ev.value')
st.p[4] = color_indices[ncolors-1]	;Init color

junk = WIDGET_BASE(ann_base, /ROW)
st.linestyle_id = CW_BSELECTOR(junk, LABEL_TOP = 'Linestyle:', $
	['Solid', 'Dots', 'Dashes', 'Dash-Dot', 'Dash-3 Dots', $
	'Long Dash'], uvalue = '@Rst.p(6) = ev.value')
st.thick_id = WIDGET_SLIDER(junk, XSIZE = sl_width, MIN=0, $
	MAX=200, TITLE = 'Thickness', /DRAG, $
	UVALUE='@Rst.p(5) = ev.value/10.')

st.instruct = WIDGET_TEXT(ann_base, value = ' ', xsize=32, ysize=1, /FRAME)

tmp_base = WIDGET_BASE(ann_base, /FRAME)
for i=0, nmodes do BEGIN
	base = WIDGET_BASE(tmp_base, /COLUMN)
	st.mode_bases[i] = base
	WIDGET_CONTROL, base, map = i eq 0
	ENDFOR

;
; Text widget
top = st.mode_bases[0]
junk = WIDGET_BASE(top, /ROW)
junk1 = WIDGET_LABEL(junk, Value = 'Text: ')
st.txt_id = WIDGET_TEXT(junk, xs = 32, ys = 1, /frame, /edit, $
	uvalue = '#Rst.txt=v[0]', value=st.txt)
junk = WIDGET_BASE(top, /ROW)
st.txt_size_id = WIDGET_SLIDER(junk, TITLE='Size', Value=0, XSIZE = sl_width, $
	MAX=100, MIN=0, /DRAG, UVALUE = '@Rst.p[7] = ev.value/10.')
st.ori_id = WIDGET_SLIDER(junk, TITLE='Orientation', Value = 0, $
	MAX = 360, min = 0, /DRAG, UVALUE = '@Rst.p[14] = ev.value', $
	XSIZE = sl_width)

st.txt_ali_id = CW_BGROUP(top, LABEL_LEFT='Alignment:', /EXCLUSIVE, /ROW, $
	['Left', 'Center', 'Right' ], SET_VALUE=0, /NO_REL, $
	UVALUE='@Rst.p[12] = ev.value/2.')
fonts = ['Simplex Roman  ', 'Simplex Greek', 'Duplex Roman', $
	'Complex Roman', 'Complex Greek', 'Complex Italic', $
	'Math & Special', 'Special', 'Gothic', 'Script', 'Complex Script', $
	'Gothic Italian', 'Gothic German', 'Cyrillic', 'Triplex Roman', $
	'Triplex Italic']
junk = WIDGET_BASE(top, /ROW)
junk1 = WIDGET_LABEL(junk, VALUE = 'Font:')
st.txt_font_id = CW_BSELECTOR(junk, fonts, $
	uvalue = '@Rst.p[13] = ev.value + 3')

; Arrow Widget
top = st.mode_bases[1]
junk = WIDGET_BASE(top, /ROW)
st.line_arrow_id = CW_BGROUP(junk, /EXCLUSIVE, /ROW, /NO_REL, $
	['Line','Arrow', 'Solid Arrow'],  $
	UVALUE='@Rst.p[17]=ev.value', SET_VALUE=0, LABEL_LEFT='Mode:')
st.head_size_id = WIDGET_SLIDER(top, TITLE='Head Size', VALUE = 0, $
	XSIZE=sl_width, MAX=100, /DRAG, UVALUE='@Rst.p[15] = ev.value/10.')


	
; Polygon Widget
top = st.mode_bases[2]
st.fill_mode_id = CW_BGROUP(top, /EXCLUSIVE, /ROW, LABEL_LEFT = 'Mode:', $
	['Draw', 'Edit', 'Delete'], /NO_REL, $
        UVALUE='@Rst.fill_mode = ev.value', SET_VALUE=0)


; Circle widget
top = st.mode_bases[3]
st.ecc_id = WIDGET_SLIDER(top, TITLE='Eccentricity', VALU=0, /DRAG, $
        XSIZE=sl_width, MIN=0, MAX=100, UVALUE='@Rst.p[18]=ev.value/10.+1')

; Square Widget  has nothing unique


for i=2, nmodes-1 do  begin	;Make polyfill controls for the bases
    top = st.mode_bases[i]
    st.poly_style[i] = CW_BGROUP(top, /EXCLUSIVE, /ROW, $
	LABEL_LEFT = 'Fill: ', /NO_REL, $
	['None', 'Solid', 'Lines'],  $
        UVALUE='@Rst.p[8]=ev.value', SET_VALUE=0)
    if i eq 2 then $
	st.spline_id = CW_BGROUP(top, /EXCLUSIVE, /ROW, /NO_REL, $
		LABEL_LEFT='Interpolation:', ['None', 'Spline'], $
		UVALUE='@Rst.p[20]=ev.value', SET_VALUE=0)
    junk = WIDGET_BASE(top, /ROW)
    st.poly_angle[i] = WIDGET_SLIDER(junk, TITLE='Line Angle', VALU=0, /DRAG, $
        MIN=0, MAX=180, UVALUE='@Rst.p[10]=ev.value', XSIZE=sl_width)
    st.poly_spacing[i] = WIDGET_SLIDER(junk, TITLE='Line Spacing', $
	VALU=0, /DRAG, MIN=0, MAX=50, UVALUE='@Rst.p[9]=ev.value', $
	XSIZE=sl_width)
    endfor



;*******************************************



; Options Widget
top = st.mode_bases[nmodes]
junk = WIDGET_BASE(top, /ROW)
junk1 = WIDGET_LABEL(junk, value = 'Grid granularity:')
junk1 = WIDGET_TEXT(junk, /EDIT, /FRAME, xsize=4, ysize=1, value='1', $
	UVALUE = '# st.granularity=ann_get_num(v(0), ev.id, 1, 128)')

junk = WIDGET_BASE(top, /COLUMN, /FRAME)
junk1 = WIDGET_LABEL(junk, VALUE= 'PostScript Options')
junk1 = WIDGET_BASE(junk, /ROW)
junk2 = CW_BGROUP(junk1, /EXCLUSIVE, /ROW, /NO_REL, $
	['Std', 'Encapsulated'], $
	UVALUE='@ st.ps_encap=ev.value', SET_VALUE=0)
junk2 = CW_BGROUP(junk1, /EXCLUSIVE, /ROW, /NO_REL, $
	['Mono', 'Color'], $
	UVALUE='@ st.ps_color=ev.value', SET_VALUE=0)
junk1 = CW_BGROUP(junk, /EXCLUSIVE, /ROW, /NO_REL, $
	['Portrait', 'Landscape'], $
	UVALUE='@ st.ps_orien=ev.value', SET_VALUE=0)
junk1 = WIDGET_BASE(junk, /ROW)
junk2 = WIDGET_LABEL(junk1, VALUE='Width:')
junk2 = WIDGET_TEXT(junk1, /EDIT, /FRAME, xsize=5, ysize=1, value='5.0', $
	UVALUE='# st.ps_width=ann_get_num(v(0), ev.id, .1, 200)')
junk2 = CW_BGROUP(junk1, /EXCLUSIVE, /ROW, /NO_REL, $
	['In', 'Cm'], UVALUE='@ st.ps_units=ev.value', SET_VALUE=0)

junk = WIDGET_BUTTON(top, VALUE='Dismiss', UVALUE='Dismiss')


wset, win
wshow, win

if n_elements(tek_colors) ge 1 then tek_color, scolor, ncolors

widget_control, ann_base, /REAL
WIDGET_CONTROL, st.color_id, SET_VALUE = color_indices[ncolors-1]   ;Set color

if keyword_set(load_file) then $	;Load a file?
	ann_xfer_file, st, load_file, /LOAD
WIDGET_CONTROL, ann_base, SET_UVALUE = st, /NO_COPY

if use_xmgr then xmanager, 'annotate', ann_base $
else xmgr_fake, ann_base, win
end