;some routines used by several of the other toolsets
 ;also some of routines used for views
 ;============================================================================
subr set_text_fields, wg, col_string, font_string, dx, dy
 n=num_elem(wg)-1
 for i=1,n do {	xmsetcolors,wg(i),col_string
		xmfont,wg(i),font_string
		xmposition,wg(i),-1,-1,dx, dy
		}
 endsubr
 ;============================================================================
subr set_boxes_red, wg
 n=num_elem(wg)-1
 for i=1,n do xmselectcolor,wg(i),'red'
 endsubr
 ;===============================================================================
subr generic_link_cb, window_widget, window_widget2
 narg = !narg
 xmtextfieldsetstring, window_widget, string($link_window)
 if narg lt 2 then return
 ;get the parameters of the array
 sq = '$data'+istring($link_window,2,2)
 if isarray(eval(sq)) eq 0 then { s = 'no data' } else {
 s = show_array(eval(sq)) }
 xmtextfieldsetstring, window_widget2, s
 endsubr
 ;===============================================================================
subr topbuttonsetup, wprefix, link_cb_string
 wts = wprefix+'widget'

 whs = wprefix+'help'
 help_file = $help_path + 'instructions.' + strreplace(wprefix,'$','',5)
 sq='widget_help, '+whs+ ','''+ help_file+''''
 bq = xmbutton(eval(wts),'Help', sq, $f4,'gray')
 xmposition,bq, 10, 10, 60, 30

 bq = xmbutton(eval(wts),'Dismiss', 'widget_dismiss, '+wts+','+whs, $f4,'gray')
 xmposition,bq, 80, 10, 64, 30

 sq = '{$link_action='''+link_cb_string+''' link_setup }'
 bq = xmbutton(eval(wts), 'Link', sq, $f4, 'darkgreen')
 xmposition, bq, 154, 10, 55, 30
 endsubr
 ;===============================================================================
func std_tlform_setup(title, wprefix, wq)
 w = xmtoplevel_form(0,0, title,0,0,5,5)
 wts = wprefix+'widget'
 equate, eval(wts), w
 wq = topbuttonsetup_tl(wprefix)
 return, w
 endfunc
  ;===============================================================================
func std_tlboardsanslink_setup(title, wprefix, wq)
 w = xmtoplevel_board(0,0, title,5,5)
 wts = wprefix+'widget'
 equate, eval(wts), w
 wq = topbuttonsetupsanslink_tl(wprefix)
 return, w
 endfunc
;===============================================================================
func std_tlboard_setup(title, wprefix, wq)
 w = xmtoplevel_board(0,0, title,5,5)
 wts = wprefix+'widget'
 equate, eval(wts), w
 wq = topbuttonsetup_tl(wprefix)
 return, w
 endfunc
;===============================================================================
func std2_tlboard_setup(title, wprefix, wq)
 ;a variation on std_tlboard_setup that eliminates the array info, more
 ;compact as a result
 w = xmtoplevel_board(0,0, title,5,5)
 wts = wprefix+'widget'
 equate, eval(wts), w
 wq = topbuttonsetup2_tl(wprefix)
 return, w
 endfunc
;===============================================================================
func std2_tlform_setup(title, wprefix, wq)
 ;a variation on std_tlform_setup that eliminates the array info, more
 ;compact as a result
 w = xmtoplevel_form(0,0, title,0,0,5,5)
 wts = wprefix+'widget'
 equate, eval(wts), w
 wq = topbuttonsetup2_tl(wprefix)
 return, w
 endfunc
 ;===============================================================================
func std_board_2links_setup(title, wprefix)
 ;used by several widgets to setup the shell, a few buttons, and a source
 ;window box. Used by fade
 wq = xmtoplevel_board(0,0, title,5,5)
 wts = wprefix+'widget'
 whs = wprefix+'help'
 help_file = $help_path + 'instructions.' + strreplace(wprefix,'$','',5)
 sq='widget_help, '+whs+ ','''+ help_file+''''
 bq = xmbutton(wq,'Help', sq, $f4,'gray')
 xmposition,bq, 0, 0, 60, 30
 bq = xmbutton(wq,'Dismiss', 'widget_popdown, '+wts+','+whs, $f4,'gray')
 xmposition,bq, 70, 0, 64, 30

 ;the first link and the source window
 ix=5	iy=45
 fw7 = xmframe(wq)
 xmposition, fw7, ix, iy
 board = xmboard(fw7, 0, 0)
 xmposition, xmlabel(board,'sources',$f4), 10, 50
 xmposition, xmlabel(board,'A',$f4), 12, 0
 xmposition, xmlabel(board,'B',$f4), 52, 0
 t1 = xmtextfield(board,'',30,'',$f3,'white')
 equate, eval(wprefix+'text1'), t1
 xmposition, t1, 2, 19, 36, 30
 link_cb_string = 'generic_link_cb,'+string(t1)
 sq = '{$link_action='''+link_cb_string+''' link_setup }'
 bq = xmbutton(wq, 'Link 1', sq, $f4, 'darkgreen')
 xmposition, bq, 144, 0, 55, 30
 xmtextfieldsetstring, t1, '0'

 ;the second link and the source window
 t1 = xmtextfield(board,'',30,'',$f3,'white')
 xmposition, t1, 42, 19, 36, 30
 equate, eval(wprefix+'text2'), t1	;note text2 here
 link_cb_string = 'generic_link_cb,'+string(t1)
 sq = '{$link_action='''+link_cb_string+''' link_setup }'
 bq = xmbutton(wq, 'Link 2', sq, $f4, 'darkgreen')
 xmposition, bq, 214, 0, 55, 30
 xmtextfieldsetstring, t1, '1'

 return, wq
 endfunc
 ;===============================================================================
func std_board_2links_alt(title, wprefix)
 ;used by several widgets to setup the shell, a few buttons, and a source
 ;window box. Used by superpose_widget, crossplot_widget, bops_widget,
 ; align_widget
 ;this is similar to std_board_2links_setup except that the text window
 ;widget names are used instead of generating the "standard" set. This allows
 ;different varieties of text windows as required by superpose and crossplot.
 ;Note that the names of the widgets must be global and need not be defined
 ;in advance (and aren't since this sets up their parent).
 wq = xmtoplevel_board(0,0, title,5,5)
 wts = wprefix+'widget'
 whs = wprefix+'help'
 help_file = $help_path + 'instructions.' + strreplace(wprefix,'$','',5)
 sq='widget_help, '+whs+ ','''+ help_file+''''
 bq = xmbutton(wq,'Help', sq, $f4,'gray')
 xmposition,bq, 0, 0, 60, 30
 bq = xmbutton(wq,'Dismiss', 'widget_popdown, '+wts+','+whs, $f4,'gray')
 xmposition,bq, 70, 0, 64, 30

 ;the first link
 link_cb_string = 'generic_link_cb,'+wprefix+'text1'
 sq = '{$link_action='''+link_cb_string+''' link_setup }'
 bq = xmbutton(wq, 'Link 1', sq, $f4, 'darkgreen')
 xmposition, bq, 144, 0, 55, 30

 ;the second link
 link_cb_string = 'generic_link_cb,'+wprefix+'text2'
 sq = '{$link_action='''+link_cb_string+''' link_setup }'
 bq = xmbutton(wq, 'Link 2', sq, $f4, 'darkgreen')
 xmposition, bq, 214, 0, 55, 30

 return, wq
 endfunc
 ;===============================================================================
func std_tlboard_2_setup(title, wprefix)
 ;used by several widgets to setup the shell, a few buttons, and a source/
 ;result window box. Used by filter, threshold, basin, noise
 wq = xmtoplevel_board(0,0, title,5,5)

 wts = wprefix+'widget'
 whs = wprefix+'help'
 help_file = $help_path + 'instructions.' + strreplace(wprefix,'$','',5)
 sq='widget_help, '+whs+ ','''+ help_file+''''
 bq = xmbutton(wq,'Help', sq, $f4,'gray')
 xmposition,bq, 0, 0, 60, 30
 bq = xmbutton(wq,'Dismiss', 'widget_popdown, '+wts+','+whs, $f4,'gray')
 xmposition,bq, 70, 0, 64, 30

 ;some text fields for window choices
 iq = xmlabel(wq,'windows for',$f5)
 xmposition,iq, 20, 50
 ix = 10		iy = 75
 fw=xmframe(wq)
 xmposition,fw, ix, iy
 board=xmboard(fw, 150, 95)
 s1 = 'source image'	s2='result image'
 text = xmtextfieldarray(board,'',$f5,'',80,10,40,35,s1,s2)
 n=num_elem(text)-1
 for i=1,n do {	xmsetcolors,text(i),'white'
		 xmfont,text(i),$f3
		 xmposition,text(i),-1,-1,40,35
		 }
 equate, eval(wprefix+'text'), text

 link_cb_string = 'generic_link_cb,'+string(text(1))
 sq = '{$link_action='''+link_cb_string+''' link_setup }'
 bq = xmbutton(wq, 'Link', sq, $f4, 'darkgreen')
 xmposition, bq, 144, 0, 55, 30

 return, wq
 endfunc
 ;============================================================================
func topbuttonsetup_tl(wprefix)
 return, topbuttonsetup(wprefix, 40, 1)
 endfunc
 ;============================================================================
func topbuttonsetup(wprefix, dyin, modein)
 ;11/30/96 (Zoe's birthday) a more complicated version of the earlier subr
 ;form, also use widget name pre-fix to avoid passing as many arguments and
 ;force more standardization
 ;returns a board widget containing 3 buttons and 2 text fields
 narg = !narg
 if narg gt 1 then dy = dyin else dy = 40
 if narg gt 2 then mode = modein else mode = 0
 wts = wprefix+'widget'
 wq = xmboard(eval(wts),0,0)
 xmposition, wq, 0, 0
 window_array_frame, wq, t1, t2, 0, dy
 equate, eval(wprefix+'text1'), t1

 whs = wprefix+'help'
 help_file = $help_path + 'instructions.' + strreplace(wprefix,'$','',5)
 sq='widget_help, '+whs+ ','''+ help_file+''''
 bq = xmbutton(wq,'Help', sq, $f4,'gray')
 xmposition,bq, 0, 0, 60, 30
 ;the mode is used to distinquish between top level and dialog widgets
 ty,'mode =', mode
 if mode eq 0 then
   bq = xmbutton(wq,'Dismiss', 'widget_dismiss, '+wts+','+whs, $f4,'gray')
   else bq = xmbutton(wq,'Dismiss', 'widget_popdown, '+wts+','+whs, $f4,'gray')
 xmposition,bq, 70, 0, 64, 30

 link_cb_string = 'generic_link_cb,'+string(t1)+','+string(t2)
 sq = '{$link_action='''+link_cb_string+''' link_setup }'
 bq = xmbutton(wq, 'Link', sq, $f4, 'darkgreen')
 xmposition, bq, 144, 0, 55, 30
 return, wq
 endfunc
 ;============================================================================
func topbuttonsetup2_tl(wprefix)
 ;a variation of topbuttonsetup without the array info
 ;returns a board widget containing 3 buttons and 1 text fields
 wts = wprefix+'widget'
 wq = xmboard(eval(wts),0,0)
 xmposition, wq, 0, 0
 ;source window #
 ix = 203		iy = 0
 t1 = xmtextfield(wq,'',1,'',$f3,'white')
 xmposition, t1, ix, iy, 40,30
 equate, eval(wprefix+'text1'), t1

 whs = wprefix+'help'
 help_file = $help_path + 'instructions.' + strreplace(wprefix,'$','',5)
 sq='widget_help, '+whs+ ','''+ help_file+''''
 bq = xmbutton(wq,'Help', sq, $f4,'gray')
 xmposition,bq, 0, 0, 60, 30
 bq = xmbutton(wq,'Dismiss', 'widget_popdown, '+wts+','+whs, $f4,'gray')
 xmposition,bq, 70, 0, 64, 30

 link_cb_string = 'generic_link_cb,'+string(t1)
 sq = '{$link_action='''+link_cb_string+''' link_setup }'
 bq = xmbutton(wq, 'Link', sq, $f4, 'darkgreen')
 xmposition, bq, 144, 0, 55, 30

 return, wq
 endfunc
 ;===============================================================================
subr topbuttonsetupsanslink, wts, whs, help_file
 sq='widget_help, '+whs+ ','''+ help_file+''''
 bq = xmbutton(eval(wts),'Help', sq, $f4,'gray')
 xmposition,bq, 10, 10, 60, 30
 bq = xmbutton(eval(wts),'Dismiss', 'widget_dismiss, '+wts+','+whs, $f4,'gray')
 xmposition,bq, 80, 10, 64, 30
 endsubr
 ;===============================================================================
func topbuttonsetupsanslink(wprefix)
 wts = wprefix+'widget'
 wq = xmboard(eval(wts),0,0)
 xmposition, wq, 0, 0
 whs = wprefix+'help'
 help_file = $help_path + 'instructions.' + strreplace(wprefix,'$','',5)
 sq='widget_help, '+whs+ ','''+ help_file+''''
 bq = xmbutton(wq,'Help', sq, $f4,'gray')
 xmposition,bq, 0, 0, 60, 30
 bq = xmbutton(wq,'Dismiss', 'widget_dismiss, '+wts+','+whs, $f4,'gray')
 xmposition,bq, 70, 0, 64, 30
 return, wq
 endfunc
 ;===============================================================================
func topbuttonsetupsanslink_tl(wprefix)
 wts = wprefix+'widget'
 wq = xmboard(eval(wts),0,0)
 xmposition, wq, 0, 0
 whs = wprefix+'help'
 help_file = $help_path + 'instructions.' + strreplace(wprefix,'$','',5)
 sq='widget_help, '+whs+ ','''+ help_file+''''
 bq = xmbutton(wq,'Help', sq, $f4,'gray')
 xmposition,bq, 0, 0, 60, 30
 bq = xmbutton(wq,'Dismiss', 'widget_popdown, '+wts+','+whs, $f4,'gray')
 xmposition,bq, 70, 0, 64, 30
 return, wq
 endfunc
 ;============================================================================
subr window_array_frame, parent, wn, warr, ix, iy
 ;convenience routine to put up a framed area for window number and
 ;a text field for the associated array information, widgets for the
 ;last 2 are returned
 fw=xmframe(parent)
 xmposition,fw, ix, iy
 board=xmboard(fw, 308, 40)
 xmposition, xmlabel(board,'window',$f4), 10, 8
 xmposition, xmlabel(board,'array',$f4), 115, 8
 wn = xmtextfield(board,'',2,'',$f3,'white')
 xmposition, wn, 70, 2, 40, 35
 warr = xmtextfield(board,'',2,'',$f3,'white')
 xmtextfieldseteditable, warr, 0
 xmposition,warr, 160, 2, 140, 35
 endsubr
 ;========================================================
subr formvstack,b1,b2,b3,b4,b5,b6,b7,b8,b9,b10,b11,b12,b13,b14,b15,b16,b17,b18
 ;works from top down
 narg=!narg
 xmattach,b1,0,    1,1,1,0
 xmattach,b2,b1,   0,0,1,0
 xmattach,b2,0,    1,1,0,0
 if narg gt 2 then for k = 3, narg do {
 	xmattach,eval('b'+string(k)),eval('b'+string(k-1)), 0,0,1,0
 	xmattach,eval('b'+string(k)),0, 1,1,0,0
 }
 xmattach,eval('b'+string(narg)),0,0,0,0,1
 endsubr
 ;========================================================
subr formhstack,b1,b2,b3,b4,b5,b6
 ;works from left to right
 narg=!narg
 xmattach,b1,0,   1,0,0,0
 xmattach,b2,b1,   1,0,-1,-1
 if narg gt 2 then xmattach,b3,b2,   1,0,-1,-1
 if narg gt 3 then xmattach,b4,b3,   1,0,-1,-1
 if narg gt 4 then xmattach,b5,b4,   1,0,-1,-1
 if narg gt 5 then xmattach,b6,b5,   1,0,-1,-1
 xmattach,eval('b'+string(narg)),0,0,1,0,0
 endsubr
 ;========================================================
subr formhstackr,b1,b2,b3,b4,b5,b6
 ;very often we want the right side widget to determine the size,
 ;so this right to left variation is provided
 ;works from right to left
 narg=!narg
 xmattach,b1,0,   0,1,0,0
 xmattach,b2,b1,   0,1,-1,-1
 if narg gt 2 then xmattach,b3,b2,    0,1,-1,-1
 if narg gt 3 then xmattach,b4,b3,    0,1,-1,-1
 if narg gt 4 then xmattach,b5,b4,    0,1,-1,-1
 if narg gt 5 then xmattach,b6,b5,    0,1,-1,-1
 xmattach,eval('b'+string(narg)),0,1,0,0,0
 endsubr
 ;========================================================
func labeled_button(callback,s,ix,iy,parent)
 b=xmbutton(parent, ' ', callback, $f4, 'red')
 xmposition,b,ix,iy,30,30
 lq = xmlabel(parent, s, $f4)
 xmposition,lq, ix+40,iy+3
 return,b
 endfunc
 ;========================================================
subr widget_help, help_widget_name, file_name
 ty,'file name: ', file_name
 if defined(help_widget_name) eq 0 then {
 help_widget_name = xmdialog_form(0,0,0, 'tool instructions',0,0,10,10)
 f3='-adobe-courier-bold-r-normal--12*'
 ls = xmtextfromfile(help_widget_name, file_name, 30, 80, f3, 'white')
 xmattach, xtparent(ls), 0,   1,1,1,1
 xmtextseteditable, ls, 0	;makes non-editable
 xtmanage, help_widget_name
 flipflop = 1
 } else {
 if flipflop eq 1 then { xtunmanage, help_widget_name   flipflop = 0 } else {
 	xtmanage, help_widget_name flipflop = 1 }
 }
 endsubr
 ;===============================================================================
subr widget_dismiss, widget_name, help_widget_name
 xtunmanage, widget_name
 if defined(help_widget_name) ne 0 then xtunmanage, help_widget_name
 if $blocking then { $blocking = 0  !motif = 0 }
 endsubr
 ;===============================================================================
subr widget_popdown, widget_name, help_widget_name
 xtpopdown, widget_name
 if defined(help_widget_name) ne 0 then xtunmanage, help_widget_name
 if $blocking then { $blocking = 0  !motif = 0 }
 endsubr
 ;===============================================================================
subr file_select
 ;set up a file selection dialog
 if defined($fs1) eq 1 then xtmanage, $fs1 else {
 s1 = 'Read and Display File'
 $fs1=xmfileselect(0, s1,'single_display','Display','fhelp','fcancel', $f3,$f5)
 }
 return
 endsubr
 ;==============================================================================
subr fcancel
 ;just unmanage, kept for older programs, now use fscancel
 xtunmanage, $fs1
 return
 endsubr
 ;========================================================
subr fscancel, win
 ;just unmanage
 xtunmanage, eval('$fs'+string(win))
 return
 endsubr
 ;========================================================
subr prompt_cb
 !motif =0
 endsubr
 ;========================================================
subr redraw_cb, win
 ;a front end for view except we do the button thing
 sq = '$view_redraw_button'+string(win)
 change_button_label, eval(sq), 'wait', 'red'
 xtloop, 2
 view, win
 change_button_label, eval(sq), 'Redraw', 'gray'
 endsubr
 ;========================================================
subr file_view, k
 ;set up a file selection dialog
 sk = string(k)
 wname = '$fv'+ sk
 if defined(eval(wname)) eq 1 then xtmanage, eval(wname) else {
 sq = 'enter path for file\nselection dialog\nor leave blank'
 sq = sq + '\nfor current directory'
 xmprompt, sq,'','prompt_cb', 1, $f7, '',!screen_width/2 -50,!screen_height/2-50
 xtloop, 1
 ;we get here after the prompt_cb lets us (it sets !motif = 0)
 dirq = $textfield_value
 xtloop, 0
 !motif = 1
 help_file = $help_path + '/instructions.fview'
 s1 = 'Viewer'+ sk
 scommand = 'display,'+ sk
 whs = wname + '_help'
 scancel = 'widget_dismiss, '+wname+','+whs
 shelp = 'widget_help, '+whs+ ','''+ help_file+''''
 iq=xmfileselect(0, s1, scommand,'Display', shelp, scancel,$f3,$f5, '', dirq)
 switch, iq, eval(wname)
 }
 return
 endsubr
 ;========================================================
subr file_load, scommand
 ;set up a file selection dialog for loading a file name into a text widget
 ;otherwise very similar to file_view
 ;scommand is the command to be done when the load file button is pressed,
 ;it should be a global string variable
 wname = '$file_load'
 if defined(eval(wname)) eq 1 then xtmanage, eval(wname) else {
 sq = 'enter path for file\nselection dialog\nor leave blank'
 sq = sq + '\nfor current directory'
 xmprompt, sq,'','prompt_cb', 1, $f7, '',!screen_width/2 -50,!screen_height/2-50
 xtloop, 1
 ;we get here after the prompt_cb lets us (it sets !motif = 0)
 dirq = $textfield_value
 xtloop, 0
 !motif = 1
 help_file = $help_path + 'instructions.fload'
 s1 = 'Load File Name'
 whs = wname + '_help'
 scancel = 'widget_dismiss, '+wname+','+whs
 shelp = 'widget_help, '+whs+ ','''+ help_file+''''
 iq=xmfileselect(0, s1, scommand,'Load File', shelp, scancel,$f3,$f5, '', dirq)
 switch, iq, eval(wname)
 }
 return
 endsubr
 ;========================================================
subr fhelp
 xmmessage,$mess3, $f7,'red'
 return
 endsubr
 ;========================================================
func ftsc_old(x)
 ;the older version of ftsc, obsolete
 ;returns a scaled byte array version of x, could be either an ft type scaling
 ;or just a specified scale range depending on $scaletype parameter
 ;check for mapping info
 hbot = .001	htop = .999	gam = 1.0
 if defined($imagemapwidget) eq 1 then {
  hbot = float(xmtextfieldgetstring ($imaptext1)
  htop = float(xmtextfieldgetstring ($imaptext2)
  gam = float(xmtextfieldgetstring ($imaptext3)
 } else { $scaletype = 2 }	 ;if widget not defined, always option 2
 ;don't be mislead by the names above, they will be interpreted according to
 ;$scaletype
 ncase $scaletype
 {  if hbot eq 0 and htop eq 0 then { hbot = min(x)  htop = !lastmax }
    return, pscale(x, hbot, htop, gam) }
 {  hbot = min(x)  htop = !lastmax
    return, pscale(x, hbot, htop, gam) }
 {  
    return, ft(x, hbot, htop, gam) }
 endcase
 endfunc
 ;========================================================
subr tvbrow, x, win, cflag
 ;used for simple displays that require the mapping parameters
 ;cflag controls compression by 2 or central area display
 if symclass(x) ne 4 then {
   errormess,'no such image'
   return }

 nx = dimen(x, 0)		ny = dimen(x,1)
 ;since we want to match the window to the image, we need to check for
 ;one of the transpose cases of !iorder
 nxx = nx  nyy = ny
 if (!iorder % 2) eq 1 then { nxx = ny   nyy = nx }
 if cflag eq 1 or cflag eq 2 then  {
	xport,win,nxx/2,nyy/2,10,10 } else { xport,win,nxx,nyy,10,10 }
 if cflag eq 0 then tv,ftsc_old(x),0,0,win else
   if cflag eq 1 tv,ftsc_old(compress(x,2)),0,0,win else
    if cflag eq 2 then tv,ftsc_old(center(x,nx/2,ny/2)),0,0,win
 xraise, win
 $window_orientation(win) = !iorder
 xflush
 endsubr
 ;========================================================
subr tvbrowsansft, x, win, cflag
 ;used for simple displays without an ft, just passes data to tv
 ;cflag controls compression by 2 or central area display
 nx = dimen(x, 0)		ny = dimen(x,1)
 ;since we want to match the window to the image, we need to check for
 ;one of the transpose cases of !iorder
 nxx = nx  nyy = ny
 if (!iorder % 2) eq 1 then { nxx = ny   nyy = nx }
 if cflag eq 1 or cflag eq 2 then  {
	xport,win,nxx/2,nyy/2,10,10 } else { xport,win,nxx,nyy,10,10 }
 if cflag eq 0 then tv, x,0,0,win else
   if cflag eq 1 tv,compress(x,2),0,0,win else
    if cflag eq 2 then tv,center(x,nx/2,ny/2),0,0,win
 xraise, win
 xflush
 endsubr
 ;=============================================================================
func subarea_radiobox(parent, ix, iy)
 widget = xmradiobox(parent,'',$f4,'','entire image','subarea #')
 xmposition,widget(0), ix, iy
 n=num_elem(widget)-1
 for i=1,n do xmselectcolor,widget(i),'red'
 xmtogglesetstate, widget(1), 1
 return, widget
 endfunc
 ;=============================================================================
func labelarray(par,font,color,w,h,ix,iy,alig,l1,l2,l3,l4,l5,l6,l7,l8,l9,l10)
 ;assemble a vertical array of labels, lots of arguments and a bit
 ;complicated to make it flexible
 nlab = !narg - 8
 if nlab le 0 then { ty,'too few args in labelarray' return, 0 }
 if nlab gt 10 then { ty,'too many labels in labelarray' return, 0 }
 warray = lonarr(nlab)
 zero, warray
 for i=0,nlab-1 do {
 ;ty,'labelarrayrj, i =',i
 iq = xmlabel(par,eval('l'+string(i+1)),font,color,1)
 warray(i) = iq
 xmalignment, iq, alig
 xmposition, iq, ix,iy+i*h,w
 }
 d, warray
 return,warray
 endfunc
 ;=============================================================================
subr badnews, s
 xmmessage,s, $f7, 'red',!screen_width/2,!screen_height/2
 endsubr
 ;=============================================================================
subr convertfordata, x1, y1, win
 ;used to convert screen boxes to limits in the data
 ;we have to check for "super views" that exceed the data space size, these
 ;are always coupled
 ;first account for any zoom and offset
 zm = $view_zoom_fac(win)
 if zm lt 0 then { zm = 1.0/abs(zm) }	;make zm fp, so have to "fix" a lot
 x1 = fix(x1/zm) + $view_ix(win)
 y1 = fix(y1/zm) + $view_iy(win)
 ;are we part of a connected set of views ?
 if $view_connect(win) then {
 	x1 = x1 + $view_connectx(win)
 	y1 = y1 + $view_connecty(win)
  }
 ;to get the coord. in the original array, need to know orientation
 iq = $view_order(win)
 nx = $view_nx(win)
 ny = $view_ny(win)
 jq = iq%4
 ;reverse direction of x and y as needed
 if jq lt 2 then { y1 = ny-1-y1 }
 if jq%2 eq 1 then { x1 = nx-1-x1 }
 ;check if a transpose
 if iq ge 4 then { switch, x1,y1 }
 ;always return integers
 x1 = rfix(x1)	y1 = rfix(y1)
 ;12/23/99 - part of a coupled set ?
 ;;12/23/99 - no longer force to be in range
 ;;force to be in range
 ;;x1 = (x1>0)<(nx-1)
 ;;y1 = (y1>0)<(ny-1)
 endsubr
 ;=============================================================================
subr convertforview, x1, y1, win
 ;used to convert limits in the data to screen boxes
 ;are we part of a connected set of views ?
 if $view_connect(win) then {
  x1 = x1 - $view_connectx(win)
  y1 = y1 - $view_connecty(win)
  }
 ;need to know orientation, apply transpose first for this direction
 iq = $view_order(win)
 ;check if a transpose
 if iq ge 4 then { switch, x1,y1 }
 jq = iq%4
 nx = $view_nx(win)
 ny = $view_ny(win)
 ;reverse direction of x and y as needed
 if jq lt 2 then { y1 = ny-1-y1 }
 if jq%2 eq 1 then { x1 = nx-1-x1 }
 ;account for any zoom and offset
 zm = $view_zoom_fac(win)
 if zm lt 0 then { zm = 1.0/abs(zm) }	;make zm fp, so have to "fix" a lot
 ;ty,'zm, $view_ix(win), $view_iy(win) =', zm, $view_ix(win), $view_iy(win)
 x1 = rfix(zm*(x1 - $view_ix(win)))
 y1 = rfix(zm*(y1 - $view_iy(win)))
 endsubr
 ;=============================================================================
subr convertbbforview, x1, y1, x2, y2, win
 ;used to convert limits in the data to screen boxes
 ;need to know orientation, apply transpose first for this direction
 ;are we part of a connected set of views ?
 if $view_connect(win) then {
  x1 = x1 - $view_connectx(win)
  y1 = y1 - $view_connecty(win)
  }
 iq = $view_order(win)
 ;check if a transpose
 if iq ge 4 then { switch, x1,y1  switch, x2,y2 }
 jq = iq%4
 nx = $view_nx(win)
 ny = $view_ny(win)
 ;reverse direction of x and y as needed
 if jq lt 2 then { y1 = ny-1-y1  y2 = ny-1-y2 }
 if jq%2 eq 1 then { x1 = nx-1-x1  x2 = nx-1-x2 }
 ;account for any zoom and offset
 zm = $view_zoom_fac(win)
 if zm lt 0 then { zm = 1.0/abs(zm) }	;make zm fp, so have to "fix" a lot
 ;ty,'zm, $view_ix(win), $view_iy(win) =', zm, $view_ix(win), $view_iy(win)
 if x1 gt x2 then switch, x1, x2
 if y1 gt y2 then switch, y1, y2
 x1 = fix(zm*(x1 - $view_ix(win)))
 y1 = fix(zm*(y1 - $view_iy(win)))
 x2 = fix(zm*(x2 +.9999 - $view_ix(win)))
 y2 = fix(zm*(y2 +.9999 - $view_iy(win)))
 endsubr
 ;=============================================================================
subr latlongupdate, win
 if $radio_state then $viewlatlongflag(win) =1  else $viewlatlongflag(win) =0
 endsubr
 ;=============================================================================
subr viewclockupdate, win
 if $radio_state then {
   $viewclockflag(win) =1 
   view_clock_box, win
 } else {
   $viewclockflag(win) =0
   xtunmanage, eval('$viewclockbox'+ string(win))
 }
 endsubr
 ;=============================================================================
subr despikeupdate, win
 ty,'despikeupdate, win =', win, ' $radio_state =', $radio_state
 if $radio_state then $viewdespikeflag(win) =1  else $viewdespikeflag(win) =0
 ty,'result =', $viewdespikeflag(win)
 endsubr
 ;=============================================================================
subr emiupdate, win
 if $radio_state then $viewemiflag(win) =1  else $viewemiflag(win) =0
 endsubr
 ;=============================================================================
subr emidebug, win
 if $radio_state then $viewemidebug(win) =1  else $viewemidebug(win) =0
 endsubr
 ;=============================================================================
subr get_despike_stuff, wtext
 ;3/24/99 - if the target doesn't exist, return default values
 ;8/3/99 - fix bugs, when a bad value encountered, set to default
 $despike_thr = 0.20
 $despike_level = 7
 $despike_niter = 3
 $despike_rms = .002
 $despike_cck = 0.0
 if isarray(wtext) then {
  xq = fix(xmtextfieldgetstring( wtext(1))) * 0.01
  if xq lt .01 or xq gt 2.0 then {
	 xmtextfieldsetstring, wtext(1), string(fix($despike_thr*100))
	 errormess,'despike threshold\nout of range'
	 } else $despike_thr = xq

  xq = fix(xmtextfieldgetstring( wtext(2)))
  if xq lt -1 or xq gt 15 then {
	 xmtextfieldsetstring, wtext(2), string($despike_level)
	 errormess,'despike level\nout of range'
	 } else $despike_level = xq

  xq = (fix(xmtextfieldgetstring( wtext(3))))
  if xq lt 0 or xq gt 15 then {
	 xmtextfieldsetstring, wtext(3), string($despike_niter)
	 errormess,'despike niter\nout of range'
	 } else $despike_niter = xq

  $despike_rms = .01*(float(xmtextfieldgetstring( wtext(4))))
  $despike_cck = float(xmtextfieldgetstring( wtext(5)))
 } endsubr
 ;=============================================================================
subr viewdespike, win
 ;extended to work with single key input as well as widget button push, this
 ;means the widget may not be defined, cope with it
 sq = '$imagedespiketext'+string(win)
 
 if defined(eval(sq)) then {
 ; if despike widget defined for this window, get the parameters
 change_button_label, $viewdespike_but(win), 'wait', 'red'
 xtloop,2
 get_despike_stuff, eval(sq)
 } else {
 ;otherwise assign defaults
 $despike_thr = 0.20
 $despike_level = 7
 $despike_niter = 3
 $despike_rms = .002
 $despike_cck = 0.0
 }
 data_str = '$data'+string(win)
 if symdtype(eval(data_str)) ne 1 then {
 	errormess,'despike only\noperates on\nI*2 arrays'  return }
 ;10/17/98 - need to check size of array if a cell rms check is requested
 rms_value = $despike_rms
 if $despike_rms ne 0.0 then {
   nx = dimen(eval(data_str),0)
   ny = dimen(eval(data_str),1)
   if (nx%8 or ny%8) then {
     ty,'despike roughness check requires 8x8 cells, not applied here'
     rms_value = 0.0
   }
 }
 if $despike_niter gt 0 then {
 xq = despike(eval(data_str), $despike_thr, $despike_level,$despike_niter,0,rms_value)
 } else { switch, xq, eval(data_str) }
 ;also apply CCK's style, based on his trace_cleanjpg
 if $despike_cck gt 0 then xq = trace_cleanjpg(xq ,$despike_cck)
 switch, xq, eval(data_str)
 xq = 0
 if defined(eval(sq)) then
 	change_button_label, $viewdespike_but(win), 'Despike', 'gray'
 endsubr
 ;=============================================================================
func despike_internal(x)
 if defined($despike_niter) eq 0 or isarray(x) eq 0 then {
 	errormess,'internal\ndespiker error' return, 0}
 if symdtype(x) ne 1 then { errormess,'despike only\noperates on I*2\arrays'
 	return, x }
 rms_value = $despike_rms
 if $despike_rms ne 0.0 then {
   nx = dimen(x,0)
   ny = dimen(x,1)
   if (nx%8 or ny%8) then {
     ty,'despike roughness check requires 8x8 cells, not applied here'
     rms_value = 0.0
   }
 }
 x = despike(x, $despike_thr, $despike_level, $despike_niter, 0, $despike_rms)
 ;also apply CCK's style, based on his trace_cleanjpg
 if $despike_cck gt 0 then x = trace_cleanjpg(x ,$despike_cck)
 return, x
 endfunc
 ;=============================================================================
subr viewemi, win
 ;the widget may not be defined, cope with it
 sq = '$imageemiwidget'+string(win)
 
 if defined(eval(sq)) then {
 ; if despike widget defined for this window, get the parameters
 change_button_label, $viewemi_but(win), 'wait', 'red'
 xtloop,2
 ;get parameters here if/when we have some
  } else {
 ;otherwise assign defaults
  ty,'no defaults'
 }
 data_str = '$data'+string(win)
 xq = remove_emi(eval(data_str), win)
 switch, xq, eval(data_str)
 xq = 0
 if defined(eval(sq)) then
 	change_button_label, $viewemi_but(win), 'Reduce EMI', 'gray'
 endsubr
 ;=============================================================================
subr viewdespike_cb, win
 viewdespike, win
 view, win
 endsubr
 ;=============================================================================
subr viewemi_cb, win
 viewemi, win
 view, win
 endsubr
 ;=============================================================================
subr viewstatsupdate, win
 if $radio_state then $viewstatsflag(win) =1  else $viewstatsflag(win) =0
 endsubr
 ;=============================================================================
subr viewstats, win
 ;recompute
 ;first check if the widget exists
 if defined(eval('$imagestatswidget'+string(win))) eq 0 then {
  ;fake a pushing of the menu
  $option_value = 0
  view_option, win
 }
 sq = viewstatstring(win)
 xmsetlabel, eval('$istatslab'+string(win)), sq
 endsubr
 ;===============================================================================
subr getarraystats(x, type, xmax, xmin, xmean, xrms, xskew, xkurt)
 type = symdtype(x)
 array_statistics, x, xmax, xmin, xmean, xrms, xskew, xkurt
 xmean = mean(x)
 xmax = max(x)		xmin = !lastmin
 xrms = sdev(x)
 endsubr
 ;===============================================================================
func viewstatstring(win)
 ;compute the stuff
 if defined(eval('$data'+istring(win, 1))) eq 0 then {
  sq = 'no data for this view'
  return, sq
 }
 sq = '$data'+istring(win, 1)
 getarraystats, eval(sq), type, xmax, xmin, xmean, xrms, xskew, xkurt
 ;we do more stuff for I type data than floating point and differently
 types = ['I*1','I*2','I*4','F*4','F*8']
 nx = dimen(eval(sq),0)		ny = dimen(eval(sq),1)
 if type le 2 then {
 sq = sprintf('%s\n%dx%d\n%15.7g\n%d\n%d\n%15.7g\n%15.7g\n%15.7g', types(type),nx, ny,xmean, xmax, xmin, xrms, xskew, xkurt)
 } else {
 sq = sprintf('%s\n%dx%d\n%15.7g\n%15.7g\n%15.7g\n%15.7g\n%15.7g\n%15.7g', types(type),nx,ny,xmean, xmax, xmin, xrms, xskew, xkurt)
 }
 return, sq
 endfunc
 ;===============================================================================
func view_stats_widget(win)
 ;4/15/98 shows some statistics about an image
 if defined(eval('$imagestatswidget'+string(win))) eq 0 then {
 pq = eval('$viewer'+string(win))
 viq = xmdialog_board(pq,0,0, 'Image Statistics for View '+string(win), 0, 0, 10, 10)
 equate, eval('$imagestatswidget'+string(win)), viq
 help_file = $help_path + '/instructions.imagestats'
 topbuttonsetupsanslink,'$imagestatswidget'+string(win),'$imagestatshelp', help_file
 ;;wq = topbuttonsetupsanslink('$imagestats')

 sw = string(win)		;a bit indirect
 bq = xmbutton(viq, 'recompute', 'viewstats,'+sw, $f4, 'gray')
 xmposition, bq, 154, 10, 80, 30
 
 s1 = 'auto update'
 iq = xmcheckbox(viq, 'viewstatsupdate,'+sw, $f4, '', s1, 1)
 xmselectcolor,iq(1),'red'
 ;bring up with auto update on (was off until we do this)
 $viewstatsflag(win) = 1
 xmtogglesetstate, iq(1), 1

 xmposition, iq(0), 0, 50
 sq = 'data type\ndimensions\nmean\nmax\nmin\nrms\nskew\nkurtosis\nentropy\ndpcm entropy'
 wlab = xmlabel(viq, sq, $f3)
 xmalignment, wlab, 0
 xmposition, wlab, 10, 90
 sq = viewstatstring(win)
 wlab = xmlabel(viq, sq, $f3)
 xmalignment, wlab, 2
 xmposition, wlab, 90, 90
 switch, wlab, eval('$istatslab'+string(win))
 return, viq
 } else { return, eval('$imagestatswidget'+string(win))   }
 endfunc
 ;===============================================================================
func view_map_widget(win)
 ;3/20/99 note, a similar widget for movie cubes also exists
 ;7/13/98 call backs in text fields to redraw
 ;3/13/98 modified to be a child of the toplevel view
 ;10/4/96 modified to work with a single view specified by win
 ;adjust the way image maps to screen
 if defined(eval('$imagemapwidget'+string(win))) eq 0 then {
 if $view_exists(win) eq 0 then {
 	errormess,'view_map_widget can''t complete\nbecause view does not exist'
	return, -1 }
 p = eval('$viewer'+string(win))
 viq = xmdialog_board(p,0,0, 'Image Mapping for View '+string(win), 0, 0, 10, 10)
 equate, eval('$imagemapwidget'+string(win)), viq
 help_file = $help_path + '/instructions.imagemap'
 topbuttonsetupsanslink, '$imagemapwidget'+string(win), '$imagemaphelp', help_file
 sw = string(win)		;a bit indirect
 scomm = 'redraw_cb,'+sw
 bq = xmbutton(viq, 'Redraw', scomm, $f4, 'gray')
 xmposition, bq, 154, 10, 60, 30
 equate, eval('$view_redraw_button'+string(win)), bq
 ;;iq = xmlabel(viq,'Adjusments only affect\nsubsequent displays or redraws', $f5)
 ;;xmposition, iq, 10, 45

 ;;ix = 10		iy = 90
 ix = 10		iy = 50
 fw=xmframe(viq)
 xmposition,fw, ix, iy
 board=xmboard(fw, 190, 54)
 ;for the multi-widget version, use 2 global arrays of internal widgets
 wlab = intarr(2)
 wtext = intarr(3)
 iq = xmlabel(board,'hbot',$f4)
 xmposition, iq, 8, 35
 wlab(0) = iq
 iq = xmtextfield(board,'',10,scomm,$f3,'white')
 xmposition,iq, 0, 4, 61, 30
 wtext(0) = iq
 iq = xmlabel(board,'htop',$f4)
 xmposition, iq, 70, 35
 wlab(1) = iq
 iq = xmtextfield(board,'',10,scomm,$f3,'white')
 wtext(1) = iq
 xmposition,iq, 62, 4, 61, 30
 xmposition, xmlabel(board,'gam',$f4), 135, 35
 iq = xmtextfield(board,'',10,scomm,$f3,'white')
 xmposition,iq, 126, 4, 61, 30
 wtext(2) = iq
 
 s1 = 'fixed range'
 s2 = 'auto min/max'
 s3 = 'histogram trim'
 s4 = 'log'
 iq = xmradiobox(viq, 'ftsc_mode_cb,'+sw, $f4, '', s1, s2, s3, s4)
 ;;xmposition, iq(0), 10, 160
 xmposition, iq(0), 10, 120
 for i=1,4 do xmselectcolor,iq(i),'red'
 switch, iq, eval('$imapradio'+string(win))

 ;add a label for the current scaling at the bottom
 ;;xmposition, xmlabel(viq,'current scaling range', $f4), 10, 285
 xmposition, xmlabel(viq,'current scaling range', $f4), 10, 245
 sq2 =fstring('%g',$view_scale_range(0,win)) -
 	+ ' to ' + fstring('%g',$view_scale_range(1,win))
 $view_label(0,win) = xmlabel(viq,sq2, $f1,'',1)
 ;;xmposition, $view_label(0,win), 10, 310
 xmposition, $view_label(0,win), 10, 270
 
 xmtextfieldsetstring, wtext(0),'.001'
 xmtextfieldsetstring, wtext(1),'.999'
 xmtextfieldsetstring, wtext(2),'1.0'
 switch, wlab, eval('$imaplab'+string(win))
 switch, wtext, eval('$imaptext'+string(win))
 return, viq
 } else { return, eval('$imagemapwidget'+string(win))   }
 endfunc
 ;===============================================================================
func view_range_widget(win)
 ;pops up a widget that shows what part of data is in view and allows user
 ;to specify in text fields when a specific range is required (hard to do
 ;with resize bars!)
 ;3/13/98 modified to be a child of the toplevel view
 if defined(eval('$viewrangewidget'+string(win))) eq 0 then {
 p = eval('$viewer'+string(win))
 viq = xmdialog_board(p,0,0, 'View Range for View '+string(win), 0, 0, 10, 10)
 equate, eval('$viewrangewidget'+string(win)), viq
 help_file = $help_path + '/instructions.viewrange'
 topbuttonsetupsanslink,'$viewrangewidget'+string(win),'$viewrangehelp',help_file
 sw = string(win)		;a bit indirect
 scb = 'view_button_resize_cb,'+sw
 bq = xmbutton(viq, 'Resize', scb, $f4, 'gray')
 xmposition, bq, 154, 10, 60, 30
 
 wlab = xmlabel(viq, '(----:----, ----:----)', $f4,'', 1)
 xmposition, wlab, 10,45
 iq = xmlabel(viq,'press resize to use\nvalues entered below', $f6)
 xmposition, iq, 30, 80

 ;s1 = 'x center'	s2 = 'y center'
 s1 = 'x base'	s2 = 'y base'
 s3 = 'width'		s4 = 'height'
 wtext = xmtextfieldarray(viq,scb,$f4,'',70,7,60,35,s1,s2,s3,s4, 6)
 xmposition, wtext(0), 10, 110
 for i=1,num_elem(wtext)-1 do {	xmsetcolors,wtext(i),'white'
	xmfont,wtext(i),$f3
	xmposition,wtext(i),-1,-1,60,35		}

 switch, wtext, eval('$viewtext'+string(win))
 switch, wlab, eval('$viewlab'+string(win))
 return, viq
 } else { return, eval('$viewrangewidget'+string(win))   }
 endfunc
 ;===============================================================================
func view_emi_widget(win)
 ;5/19/2000 - first version
 $win = win
 if defined(eval('$imageemiwidget'+string(win))) eq 0 then {
 compile_file, getenv('ANA_WLIB') + '/emi_widgettool.ana'
 }
 return, eval('$imageemiwidget'+string(win))
 endfunc
 ;===============================================================================
func view_despike_widget(win)
 ;3/22/99 - there is a similar widget for movie cube creation
 ;5/20/98 - starting with the Mk I despiker, hopefully will be improved
 $win = win
 if defined(eval('$imagedespikewidget'+string(win))) eq 0 then {
 compile_file, getenv('ANA_WLIB') + '/despike_widgettool.ana'
 }
 return, eval('$imagedespikewidget'+string(win))   ; removed spurious
						   ; } LS 11jul2000
 endfunc
 ;===============================================================================
func movie_despike_widget(dum)
 ;5/20/98 - based on view_despike, this is for correcting already made
 ;movie cubes, needs more options
 win = 11
 sw = string(win)		;a bit indirect
 if defined(eval('$mvspikewidget'+sw)) eq 0 then {
 viq = xmdialog_board(0,0,0, 'Despike for Movie window '+sw, 0, 0, 10, 10)
 equate, eval('$mvspikewidget'+sw), viq
 help_file = $help_path + '/instructions.mvdespike'
 topbuttonsetupsanslink, '$mvspikewidget'+sw, '$mvspikehelp', help_file
 scb = 'mvdespike_cb'
 bq = xmbutton(viq, 'Despike', scb, $f4, 'gray')
 xmposition, bq, 154, 10, 80, 30
 $mvdespike_but = bq

 iq = xmlabel(viq,'applies despike to current movie cube\nbe careful!', $f5)
 xmposition, iq, 10, 45

 s1 = 'threshold in\n% (0 to 100)'
 s2 = 'level'
 s3 = '# of iterations'
 wtext = xmtextfieldarray(viq,'',$f4,'',100,7,60,35,s1,s2,s3, 3)
 xmposition, wtext(0), 10, 80
 for i=1,num_elem(wtext)-1 do {	xmsetcolors,wtext(i),'white'
	xmfont,wtext(i),$f3
	xmposition,wtext(i),-1,-1,60,35		}
 ;some defaults
 xmtextfieldsetstring, wtext(1),'15'
 xmtextfieldsetstring, wtext(2),'7'
 xmtextfieldsetstring, wtext(3),'3'
 switch, wtext, $mvdespiketext11
 return, viq
 } else { return, eval('$mvdespikewidget'+string(win))   }
 endfunc
 ;===============================================================================
func view_solar_widget(win)
 ;11/18/98 - draw lat/long lines on image, other options to be added
 if defined(eval('$viewsolarwidget'+string(win))) eq 0 then {
 p = eval('$viewer'+string(win))
 wg = xmdialog_board(p,0,0, 'Solar Coordinates '+string(win), 0, 0, 10, 10)
 equate, eval('$viewsolarwidget'+string(win)), wg
 help_file = $help_path + '/instructions.viewsolar'
 topbuttonsetupsanslink,'$viewsolarwidget'+string(win),'$viewsolarhelp',help_file
 s = string(win)		;a bit indirect
 scb = 'stony_trace,'+s
 bq = xmbutton(wg, 'draw\nlat/long', scb, $f4, 'gray')
 xmposition, bq, 0, 50, 0, 0
 
 s1 = 'auto'
 iq = xmcheckbox(wg, 'latlongupdate,'+s, $f4, '', s1, 1)
 xmselectcolor,iq(1),'red'
 ;bring up with auto update off
 $viewlatlongflag(win) = 0
 xmtogglesetstate, iq(1), 0
 xmposition, iq(0), 80, 50
 s = 'R      \nB      \nLat     \nLong    \nCarr'
 $viewsolarlabeltxt(win) = xmlabel(wg, s, $f9,'', 1)
 xmalignment, $viewsolarlabeltxt(win), 0
 xmposition, $viewsolarlabeltxt(win), 10, 100
 return, wg
 } else { return, eval('$viewsolarwidget'+string(win))   }
 endfunc
 ;===============================================================================
func view_clock_widget(win)
 ;5/9/99 - control a clock/date box for the view
 s = string(win)		;a bit indirect
 if defined(eval('$viewclockwidget'+s)) eq 0 then {
 p = eval('$viewer'+s)
 wg = xmdialog_board(p,0,0, 'Clock Box Options for View '+s, 0, 0, 10, 10)
 equate, eval('$viewclockwidget'+s), wg
 help_file = $help_path + '/instructions.viewclock'
 topbuttonsetupsanslink, '$viewclockwidget'+s, '$viewclockhelp',help_file
 
 s1 = 'show time/date'
 iq = xmcheckbox(wg, 'viewclockupdate,'+s, $f4, '', s1, 1)
 xmselectcolor,iq(1),'red'
 ;bring up with auto update off
 $viewclockflag(win) = 0
 xmtogglesetstate, iq(1), 0
 xmposition, iq(0), 10, 50
 return, wg
 } else { return, eval('$viewclockwidget'+s)   }
 endfunc
 ;===============================================================================
subr view_clock_box(win)
 ;make the actual clock box, a separate widget from the control
 s = string(win)
 sq = '$viewclockbox'+ s
 if defined(eval(sq)) eq 0 then {
 p = eval('$viewer'+s)
 wq = xmdialog_board(p, 0,0, sprintf('view %d', win),2,2)
 lq = xmlabel(wq,'1998-Apr-02\n01:00:00',$f9)
 xmposition, lq, 2, 2
 xmsetcolors, wq, 'white'
 xmsetcolors, lq, 'white'
 equate, eval(sq), wq
 equate, eval('$viewclockboxtext'+s), lq
 }
 xtmanage, eval(sq)
 view_set_time, win
 endsubr
 ;============================================================ 
subr view_set_time(win)
 sq = '$viewclockboxtext'+ string(win)
 t =$view_tai(win)
 if t le 0.0 then s = 'time not\nknown' else {
   s = strtok(date_from_tai(t,0,1),'U') }
 xmsetlabel, eval(sq), s
 xtmanage, eval('$viewclockbox'+ string(win))
 endsubr
 ;============================================================ 
subr write_error, name
 errormess,'problem writing file\n'+name+'\ncheck path and privilege'
 endsubr
 ;============================================================ 
subr zap_view_data, win
 ;zero some view time and position info
 $view_tai(win) = 0
 $view_sun_xc(win) = 0
 $view_sun_yc(win) = 0
 $view_sun_pixel_scale(win) = 0
 endsubr
 ;============================================================ 
subr view_range_update, win
 ;load up the view range widget with the new values
 ;have a view range widget up, have to load it too
 nx = $view_nx(win)
 ny = $view_ny(win)
 ix = $view_ix(win)	iy = $view_iy(win)
 ;are we part of a connected set of views ?
 ;note that we want (ix, iy) as taken from array by tvplane, so the connect
 ;deltas have to be applied
 iq = $view_order(win)
 if $view_connect(win) then {
 	ix = ix + $view_connectx(win)
 	iy = iy + $view_connecty(win)
 }
 ix2 = ix + $view_width(win) -1
 iy2 = iy + $view_height(win) -1
 jq = iq%4
 ;reverse direction of x and y as needed, note that since ix and iy
 ;are coordinates in the array (not in the super view, if any), we use
 ;nx, ny for the reverseals
 if jq lt 2 then { xq=iy	iy = ny-1-iy2	iy2 = ny-1-xq }
 if jq%2 eq 1 then { xq=ix	ix = nx-1-ix2	ix2 = nx-1-xq }
 ;check if a transpose
 if iq ge 4 then { switch, ix, iy	switch, ix2, iy2 }

 sq = sprintf('( %d:%d, %d:%d )', ix, ix2, iy, iy2
 xmsetlabel, eval('$viewlab'+string(win)), sq
 wtext = eval('$viewtext'+string(win))
 xmtextfieldsetstring, wtext(1), string(ix)
 xmtextfieldsetstring, wtext(2), string(iy)
 xmtextfieldsetstring, wtext(3), string(ix2 -ix +1)
 xmtextfieldsetstring, wtext(4), string(iy2 -iy +1)
 endsubr
 ;========================================================
subr viewrescalechoices_cb
 ;used to put a particular "well known" scale into the text box
 xmtextsetstring, $viewrescale_text, string($common_scales($option_value))
 endsubr
 ;============================================================ 
subr viewrescale_cb
 if $radio_state then {
 $viewrescale_flag = $radio_button
 ty,'$viewrescale_flag =', $viewrescale_flag
 }
 ;;widget_dismiss, $viewrescalewidget, $viewrescalehelp
 endsubr
 ;============================================================ 
subr view_adjust_scales, scs, in
 if defined($viewrescalewidget) eq 0 then {
 compile_file, getenv('ANA_WLIB') + '/viewrescalewidgettool.ana'
 }
 xmsetlabel, $viewrescale_lab, -
  sprintf('Inconsistent spatial scales for a set of connected\nviews. Range %6.3f to %6.3f. We can:',min(scs),max(scs))
 xmsetmodal, $viewrescalewidget, 1
 xtmanage, $viewrescalewidget
 xmtogglesetstate, $viewrescale_rb(1), 1, 0
 ;;xtpopup, $viewrescalewidget
 ;the modal above prevents any other widgets from working, also have
 ;to stop execution here until user responses to it
 $blocking = 1	;used by dismiss
 xtloop, 1
 ;we get here after the viewrescale_cb lets us (it sets !motif = 0)
 !motif = 1
 if $viewrescale_flag then {
  ;we have to do some rescaling, this gets involved
  ;uses the $viewrescale_flag to decide action
  if $viewrescale_flag eq 3 then {
   newsc = float(xmtextfieldgetstring($viewrescale_text))
   }
  if $viewrescale_flag eq 1 then newsc = max(scs)
  if $viewrescale_flag eq 2 then newsc = min(scs)
  if newsc le 0.0 then {
    errormess,sprint('bad common newsc = %12.3g\nrescaling canceled',newsc)
    $viewrescale_flag = 0
    return
   }
  ;now convert all windows to scale but allow some margin
  nw = num_elem(in) - 1
  for j=0, nw do {
    i = in(j)
    sc = $view_sun_pixel_scale(i)
    if sc le 0 then {
      errormess,sprint('invalid scale (%12.3g)\nfor window %d',sc,i)
    } else {
     xq = sc/newsc	;this is magnification to apply
     ;see if "close enough"
     if abs(xq - 1.0) gt 0.001 then {
       ;save old data for an undo, similar to code in view_option in toolset6
       data_str = '$data'+string(i)
       save_str = '$save_last'+string(i)
       equate, eval('$save_sc'+string(i)), sc
       zq = mag3( eval(data_str), xq)
       $view_sun_pixel_scale(i) = newsc
       switch, eval(save_str), eval(data_str)
       switch, zq, eval(data_str)
       zq = 0
       ty,'ready to view #', i
       view, i
     }
    }
  }
 }
 endsubr
 ;============================================================ 
subr connect_adjust
 ; examine all the deltas and determine window sizes and biased offsets
 ; the offsets are computed for iorder = 0 but then may be modified for
 ; the current orientation
 ;2/10/2001 - try to do something sensible if the scales don't match
 nw = num_elem($view_connect) - 1
 ;check the scales in a preliminary loop
 nc = 0
 xc = 0
 for i=0, nw do {
   ;note, supports only one match or connection set
   if $view_connect(i) and $view_exists(i) then {
   yc = $view_sun_pixel_scale(i)
   if nc then {
    scs = [scs, yc]
    in = [in, i]
   } else {
    scs = yc
    in = i
   }
   nc = nc + 1   
   }
 }

 zero, $view_connectx, $view_connecty
 zeroifundefined, $view_super_nx, $view_super_ny
 zero, $view_super_nx, $view_super_ny
 nw = num_elem(in)-1
 ty,'nc =', nc
 ;possible to get here with no connection at, just return in that case
 if nc le 0 then return

 ;also possible to have just one
 if nc le 1 then return
 	;;zero, $view_connectx, $view_connecty
 	;;return }

 ;now check if the scales are nearly all the same, define no adjustment
 ;as default, any adjustment will affect window properties
 $viewrescale_flag = 0
 yc = mean(scs)
 if max(abs(scs-yc)) gt 0.01*yc then view_adjust_scales, scs, in
 
 for j=0, nw do {
    i = in(j)
    ;get the extreme ranges for this window
    xc = 0.0
    yc = 0.0
    nx = $view_nx(i)
    ny = $view_ny(i)
    ;the $view_nx and ny are exchanged when the view is a transpose, for
    ;the connectx and y we want the normal situation
    if $view_order(i) ge 4 then { switch, nx, ny }
    nx2 = float(nx-1)*0.5
    ny2 = float(ny-1)*0.5
    sc = $view_sun_pixel_scale(i)
    if sc gt 0 then {
     sc = 1./sc
     xc = $view_sun_xc(i)*sc
     yc = $view_sun_yc(i)*sc
     }
    ;also check for additional offsets, these work in same direction as
    ;solar coordinates (so that the coordinates can be entered as these
    ;offsets when the coords are unknown and therefore set to 0)
    ;note that these are in pixels however
    if $view_offset_widgets(0, i) ne 0 then {
     xc = xc + rfix(float(xmtextfieldgetstring($view_offset_widgets(0, i))))
     yc = yc + rfix(float(xmtextfieldgetstring($view_offset_widgets(1, i))))
    }
    
    x1 = xc - nx2	;floating point
    x2 = xc + nx2
    $view_connectx(i) = rfix(x1)
    y1 = yc - ny2
    y2 = yc + ny2
    ;remember that for display, y runs from top to bottom of screen
    $view_connecty(i) = rfix(y2)
    if j then {
     xleft = xleft < x1
     xright = xright > x2
     ytop = ytop > y2
     ybot = ybot < y1
    } else {
     xleft = x1
     xright = x2
     ybot = y1
     ytop = y2
    }
   }
 ;;ty,'xleft, xright =', xleft, xright
 ;;ty,'ybot, ytop =', ybot, ytop
 ;;ty,'x,y range =', xright - xleft, ytop - ybot
 $view_super_nx = rfix(xright - xleft + 1.0)
 $view_super_ny = rfix(ytop - ybot + 1.0)
 $view_connectx(in) = rfix(xleft - $view_connectx(in))
 $view_connecty(in) = rfix($view_connecty(in) - ytop )
 ;;ty,in, $view_connectx(in), $view_connecty(in)
 ;but, we also make $view_connectx and y dependent on the orientation, this
 ;looks like it simplifies some of the translations made for convertfordata
 ;so, adjust each to the present orientation of its view, these may be
 ;different if the windows were not previously connected

 for j=0, nw do {
  i = in(j)
  ;;t,'j,i =', j,i
  nx = $view_nx(i)
  ny = $view_ny(i)
  reverse_flag = $view_order(i) % 4
  transpose_flag = $view_order(i) / 4
  if transpose_flag then {
  	xq = $view_connectx(i)
  	$view_connectx(i) = $view_connecty(i)
  	$view_connecty(i) = xq
  	nxs = $view_super_ny
  	nys = $view_super_nx
   } else {
  	nxs = $view_super_nx
  	nys = $view_super_ny
   }
  if reverse_flag gt 0 then {
    ncase reverse_flag-1
     {	$view_connectx(i) = nx - nxs - $view_connectx(i)  }
     {	$view_connecty(i) = ny - nys - $view_connecty(i)  }
     {	$view_connectx(i) = nx - nxs - $view_connectx(i)
	$view_connecty(i) = ny - nys - $view_connecty(i)  }
    endcase    
    }
 
 }
 ;and force a display of result
 slave_align, in(0)
 endsubr
 ;=============================================================================
subr viewsetconnect_on, win
   if win gt 14 or win lt 0 then {
      errormess,'not a connectable window'
      return }
   $view_connect(win) =1 
   connect_adjust
   xmsetcolors, $view_scv(win),'slateblue3'
   xmsetcolors, $view_sch(win),'slateblue3'
 endsubr
 ;=============================================================================
subr viewsetconnect_off, win
   if win gt 14 or win lt 0 then return
   $view_connect(win) =0 
   connect_adjust
   xmsetcolors, $view_scv(win),'skyblue3'
   xmsetcolors, $view_sch(win),'skyblue3'
 endsubr
 ;=============================================================================
subr viewconnectupdate, win
 if $radio_state then {
   viewsetconnect_on, win
 } else {
   viewsetconnect_off, win
 }
 endsubr
 ;=============================================================================
subr zap_connections, win
 nw = num_elem($view_connect)-1
 match = $view_connect(win)	; just one type at present but ...
 for i=0, nw do {
   if $view_connect(i) eq match then {
    $view_connect(i) = 0
    xmsetcolors, $view_scv(win),'skyblue3'
    xmsetcolors, $view_sch(win),'skyblue3'
   }
   xmtogglesetstate, $view_offset_widgets(2,i), 0
  }
 connect_adjust
 endsubr
 ;=============================================================================
subr view_zero_offsets
 ;zero the text in any of the widgets that exist
 nw = num_elem($view_connect)-1
 for i=0, nw do {
  if $view_offset_widgets(0, i) ne 0 then {
   xmtextfieldsetstring, $view_offset_widgets(0, i), ''
   xmtextfieldsetstring, $view_offset_widgets(1, i), ''
  }
 }
 connect_adjust
 endsubr
 ;===============================================================================
func view_connect_widget(win)
 ;12/20/99 - connection options for this view
 s = string(win)		;a bit indirect
 if defined(eval('$viewconnectwidget'+string(win))) eq 0 then {
 p = eval('$viewer'+s)
 wg = xmdialog_board(p,0,0, 'Connection for View '+s, 0, 0, 10, 10)
 equate, eval('$viewconnectwidget'+s), wg
 help_file = $help_path + '/instructions.viewconnections'
 topbuttonsetupsanslink, '$viewconnectwidget'+s, '$viewconnecthelp', help_file
 
 s1 = 'include in connected set'
 iq = xmcheckbox(wg, 'viewconnectupdate,'+s, $f4, '', s1, 1)
 xmselectcolor,iq(1),'red'
 ;bring up with current status of connection (it may already have been
 ;set elsewhere)
 xmtogglesetstate, iq(1), $view_connect(win)
 $view_offset_widgets(2,win) = iq(1)
 xmposition, iq(0), 10, 50
 lq = xmlabel(wg,'additional offsets (pixels)',$f4)
 xmposition, lq, 40, 80
 f0 = xmform(wg, 0, 0, 0, 5)
 xmposition, f0, 0, 100
 f1 = xmform(f0,0,0,10,0)
 f2 = xmform(f0,0,0,10,0)
 t1 = xmtextfield(f1,'',12,'connect_adjust',$f3, 'white')
 $view_offset_widgets(0,win) = t1
 xmposition,t1, 0, 0, 70, 30
 l1 = xmlabel(f1,'x', $f4)
 b1 = xmbutton(f1, 'Apply', 'connect_adjust', $f4, 'gray')
 xmposition, b1, 0, 0, 60, 30
 t2 = xmtextfield(f2,'',12,'connect_adjust',$f3, 'white')
 $view_offset_widgets(1,win) = t2
 xmposition,t2, 0, 0, 70, 30
 l2 = xmlabel(f2,'y', $f4)
 formhstackr, b1, t1, l1
 formhstackr, t2, l2
 f3 = xmform(f0,0,0,0,0,0,10)
 bd1 = xmboard(f3, 0, 0)
 b3 = labeled_button('view_zero_offsets', 'zero all additional offsets', 0,0,bd1)
 ;1/6/00 - add a button to disable all connections
 f4 = xmform(f0,0,0,0,0,0,10)
 bd1 = xmboard(f4, 0, 0)
 b3 = labeled_button('zap_connections,'+s, 'disable all connections', 0,0,bd1)
 ;;formvstack, f1, f2, f3
 xmattach,f1,0,    1,0,1,0
 xmattach,f2,f1,   0,0,1,0
 xmattach,f2,0,    1,0,0,0
 xmattach,f3,f2,   0,0,1,0
 xmattach,f4,f3,   0,0,1,0
 xmattach,f4,0,    1,0,0,1
 
 return, wg
 } else { return, eval('$viewconnectwidget'+s)   }
 endfunc
 ;============================================================ 
subr slave_align, win
 ;win is the window that the user adjusted, the others must align
 match = $view_connect(win)	; just one type at present but ...
 nw = num_elem($view_connect)-1
 ;get the zoom state, note that more than one might be
 ;set in win at this stage, so do the conversion instead of getstate
 if !tvplanezoom lt 0 then {
  if !tvplanezoom eq -4 then iz = 0 else iz =1
  } else {
  if !tvplanezoom lt 5 then iz = !tvplanezoom + 1 else {
   if !tvplanezoom eq 8 then iz = 6 else iz = 7 } }
 nz = dimen($view_zoom_widgets,0)-1
 vorder = $view_order(win)
 nxs = $view_super_nx
 nys = $view_super_ny
 if vorder ge 4 then { switch, nxs, nys }
 for i=0, nw do {
   isw = string(i)
   if $view_connect(i) eq match then {
     if i ne win then {
     ; may have to resize if not the adjusted window
     ; and set scrollbars
     ; and change the orientation
     this_order = $view_order(i)
     if vorder ne this_order then {
      re_orient, this_order, vorder, i 
      xmsetoptionselection, $view_order_widgets(0,i), -
      	$view_order_widgets(vorder+1,i)
      }
     $view_zoom_fac(i) = !tvplanezoom
     $view_xsize(i) = $view_xsize(win)
     $view_ysize(i) = $view_ysize(win)
     ix = $view_ix(win)
     iy = $view_iy(win)
     $view_ix(i) = ix
     $view_iy(i) = iy
     draw_x = $view_width(win)
     $view_width(i) = draw_x
     draw_y = $view_height(win)
     $view_height(i) = draw_y
     ;because the xmsize will generally call view_resize_cb, we must get
     ;the centers done here
     $view_ixc(i) = $view_ixc(win)
     $view_iyc(i) = $view_iyc(win)
     xmscrollbarsetvalues, $view_sch(i), ix, nxs, draw_x, .5*draw_x
     xmscrollbarsetvalues, $view_scv(i), iy, nys, draw_y, .5*draw_y
     xmsize, $view_draw(i), $view_xsize(i), $view_ysize(i)

     for j = 0, nz do {
      if j eq iz then state = 1 else state = 0
      xmtogglesetstate, $view_zoom_widgets(j,i), state, 0
      }
     }
   if defined(eval('$viewrangewidget'+isw)) eq 1 then view_range_update, i
   ix = $view_ix(i) + $view_connectx(i)
   iy = $view_iy(i) + $view_connecty(i)
   ;;ty,'i, $view_ix, $view_iy, $view_connectx, $view_connecty',i,$view_ix(i), -
   ;;	$view_iy(i), $view_connectx(i), $view_connecty(i)
   ;;ty,'i, ix, iy =', i, ix,iy
   tvplane, eval('$view_data'+isw), 0, ix, iy, i
 } }
 endsubr
 ;============================================================ 
func get_nt(x)
 ;get nt for either a cube or a symarr
 if  issymarr(x) then nt = num_elem(x) else
 	nt=dimen(x,2)
 return, nt
 endfunc
 ;========================================================
subr fviewer_cb, mode
 ;$num_file_viewers is # of existing file viewers
 if mode eq 0 then {
   sw = string($num_file_viewers)
   $num_file_viewers += 1
   sq = '$fs'+sw
   s1 = 'File Reader '+string($num_file_viewers)+' for Selected View'
   iq=xmfileselect(0, s1,'read_file_current','Load','finspect','fscancel,'+sw, -
   	$f3,$f5, '', '', 'Inspect')
   equate, eval(sq), iq
   return
  }
 ;otherwise we just pop up all existing ones
 for i=1,$num_file_viewers do {
  widg = eval('$fs'+string(i-1))
  xtmanage, widg
  xtpopup, widg
 }
 endsubr
 ;============================================================ 
