pro ctime_get_exact_data,var,v,t,pan,hx,hy,subvar,yind,yind2,z,$
         spec=spec,dtype=dtype,load=load
;
;NAME:
;   ctime_get_exact_data
;PROCEDURE:     ctime_get_exact_data
;PURPOSE:       Get a data structure for ctime.  if var is a string or a strarr,
;               create a structure of data structures.
;               Get the new values for hx and hy, the crosshairs position.
;               Also check the spec option.
;               ctime need never see the actual data structures.
;     All work is done with pointers to reduce data duplication
;               and increase speed.
;COMMON BLOCKS: CTIME_COMMON
;HISTORY:       First appeared in ctime version 1.29
;
@tplot_com
common ctime_common, ptr        ;this should NOT appear in ctime, it is local
;if dtype eq 1 then ptr is a struct of pointers containing the values:
;x = *ptr.x, y = *ptr.y, (v = *ptr.v)
;if dtype eq 3 then ptr is more complex:
;say data='Np Ne':  get_data,'Np',ptr=np_ptr & get_data,'Ne',ptr=ne_ptr
;ptr={n:2,d1:{x:np_ptr.x,y:np_ptr.y,name:'Np'},$
;         d2:{x:ne_ptr.x,y:ne_ptr.y,name:'Ne'}}

  time_scale  = tplot_vars.settings.time_scale
  time_offset = tplot_vars.settings.time_offset
  ytype       = tplot_vars.settings.y[pan].type

  ;if this is a new panel, load the data
  if keyword_set(load) then begin
    get_data,var,dtype=dtype,alim=alim,ptr=ptr
    spec = 0 & str_element,alim,'spec',spec
    case dtype of
      1:  begin
      endcase
      2:  begin   ;note that if dtype eq 2 ctime_get_exact_data does nothing
      endcase     ;ie:  ctime bahaves as if exact eq 0
      3:  begin
        get_data,var,data=var_str
        if ndimen(var_str) eq 0 then var_str = tnames(var_str) ; str_sep(strcompress(var_str),' ')
        nd2 = n_elements(var_str)
        ptr = {n:nd2}
        for i=0,nd2-1 do begin
          get_data,var_str[i],ptr=subptr,dtype=subdtype,alim=subalim
          str_element,alim,'spec',subspec
          if not keyword_set(subspec) then subspec = 0
          if subdtype ne 1 then ptr.n = ptr.n-1 $ ;too limiting...
          else begin
            tag = 'd'+strtrim(i,2)
            str_element,/add,subptr,'spec',subspec    ;add spec to substruct
            str_element,/add,subptr,'name',var_str[i] ;add var name to substruct
            str_element,/add,ptr,tag,subptr           ;add substruct to struct
          endelse
        endfor
      endcase
      else:  dprint, 'Invalid value for dtype: ',dtype
    endcase
  endif

  ;get the new values for: v,t,z,yind2,hx,hy,subvar
  yind2 = -1
  subvar = ''
  yind = 0l                       ;zero the time index
  case dtype of                   ;what type of tplot var do we have?
    1: begin
      dt = abs(*ptr.x-t)              ;find the closest data.x to t
      mini = min(dt,yind,/nan)             ;get the index of the min dt
      t = (*ptr.x)[yind]
      tags = tag_names(ptr)
      wy = where(tags EQ 'Y')
      wv = where(tags EQ 'V')
      if not spec then begin
        if dimen2(*ptr.y) gt 1 then begin ;if y 2D, get nearest line
          if finite(v) then begin
            if ytype eq 0 then dy = abs((*ptr.y)[yind,*]-v) $ ;lin scale plot
            else dy = abs(alog((*ptr.y)[yind,*])-alog(v))     ;log scale plot
            mini = min(dy,yind2,/nan)             ;get index of nearest y
            v = float((*ptr.y)[yind,yind2])
          endif
        endif else v = float((*ptr.y)[yind])
      endif else begin                ;this is a specplot
        if finite(v) then begin
          if wv[0] NE -1 then begin
            if dimen2(*ptr.v) eq 1 then vr = *ptr.v $
            else vr = reform((*ptr.v)[yind,*])
          endif else begin
            vr = findgen(n_elements((*ptr.y)[0,*]))
          endelse
          if ytype eq 0 then mini = min(abs(vr-v),yind2,/nan) $ ;lin scale plot
          else mini = min(abs(alog(vr)-alog(v)),yind2,/nan)     ;log scale plot
          v = float(vr[yind2])
          z = float((*ptr.y)[yind,yind2])
        endif
      endelse
      t_scale = (t-time_offset)/time_scale
      hx = data_to_normal(t_scale,tplot_vars.settings.x)
      hy = data_to_normal(v,      tplot_vars.settings.y[pan])
    endcase
    2: begin                        ;not written yet
    endcase
    3: begin                        ;var is a string or strarr of vars
      yinds = lonarr(ptr.n)
      t2    = dblarr(ptr.n)
      v2    = fltarr(ptr.n)+v        ;important for when v is NaN
      for i=0,ptr.n-1 do begin       ;find the substr with the nearest time
        mini = min(abs(*ptr.(i+1).x-t),yind,/nan)
        yinds[i] = yind
        t2[i] = (*ptr.(i+1).x)[yind]
      endfor
      dt  = abs(t2-t)
      sdt = sort(dt)
      w = where(dt eq dt[sdt[0]],wc)  ;see if several have the same minimum dt

      if finite(v) then begin
        if wc eq 1 then begin           ;if one substr to consider...
          j = sdt[0] & yind = yinds[j]
          if dimen2(*ptr.(j+1).y) gt 1 then begin ;if y 2D, get nearest line
            if ytype eq 0 then dy = abs((*ptr.(j+1).y)[yind,*]-v) $ ;lin scale y
            else dy = abs(alog((*ptr.(j+1).y)[yind,*])-alog(v))     ;log scale y
            mini = min(dy,yind2,/nan)             ;get index of nearest y
            v2[j] = (*ptr.(j+1).y)[yind,yind2]
          endif else v2[j] = (*ptr.(j+1).y)[yind]
        endif else begin                ;if multiple substrs to consider...
          j = w[0]
          y  = ((*ptr.(j+1).y)[yinds[j],*])[*] ;[*] works better than REFORM
          if ytype eq 0 then dy = abs(y-v) else dy = abs(alog(y)-alog(v))
          mini = min(dy,yind2,/nan)
          for i=1,wc-1 do begin
            y2  = ((*ptr.(w[i]+1).y)[yinds[w[i]],*])[*]
            if ytype eq 0 then dy = abs(y2-v) else dy = abs(alog(y2)-alog(v))
            mini2 = min(dy,yind22,/nan)
            if mini2 lt mini then begin
              j = w[i] & mini = mini2 & yind2 = yind22
            end
          endfor
          v2[j] = (*ptr.(j+1).y)[yinds[j],yind2]
          if dimen2(*ptr.(j+1).y) eq 1 then yind2 = -1
        endelse
      endif else j = sdt[0]
      t = t2[j]
      v = float(v2[j])
      subvar = ptr.(j+1).name
      t_scale = (t-time_offset)/time_scale
      hx = data_to_normal(t_scale,tplot_vars.settings.x)
      hy = data_to_normal(v,      tplot_vars.settings.y[pan])
    endcase
    else: dprint, 'Invalid value for dtype: ',dtype
  endcase
  yind = long(yind)
  yind2 = fix(yind2)
end



function time_round,time,res,precision=prec,$  ; res must be a scalar!
   days    = days   ,$       ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   hours   = hours  ,$       ; Keywords for setting time granularity.
   minutes = minutes,$       ;
   seconds = seconds         ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

res=abs(res[0])

prec=6
if res eq 0 then return,time  ; don't round

res0= [0,[1,2,5,10,20,30,60]*1d,[2,5,10,20,30,60]*60d,[2,4,6,12,24]*3600d]
prc0=-[0, 0,0,0, 0, 0, 0, 1,     1,1, 1, 1, 1, 2,      2,2,2, 2, 3]
n = n_elements(res0)
resn = floor(interp(findgen(n),res0,res))

if resn  le 0 then begin
  prec = -floor(alog10(res*1.0001d)) < 10
  res = 10d^(-prec)
endif else begin
  res  = res0[resn < (n-1)]
  prec = prc0[resn < (n-1)]
endelse

if keyword_set(days)    then res = days*86400.d
if keyword_set(hours)   then res = hours*3600.d
if keyword_set(minutes) then res = minutes*60.d
if keyword_set(seconds) then res = seconds*1.d

rtime = time_double(time)

if res ge 1 then return, res * round(rtime/res)
time0 = round(time)
return, time0 + res * round((rtime-time0)/res)

end



;+
;PROCEDURE:   ctime,time,y,z
;INPUT:
;    time: Named variable in which to return the selected time (seconds
;          since 1970)
;    y:    Named variable in which to return the y value
;    z:    Named variable in which to return the z value
;KEYWORDS:
;    PROMPT:  Optional prompt string
;    NPOINTS: Max number of points to return
;    PSYM:    If set to a psym number, the cooresponding psym is plotted at
;             selected points
;    SILENT:  Do not print data point information
;    PANEL:   Set to a named variable to return an array of tplot panel numbers
;             coresponding to the variables points were chosen from.
;    XNORM:   Set to a named variable to return an array of normalized x
;             coordinates of each button-click.
;    YNORM:   Set to a named variable to return an array of normalized y
;             coordinates of each button-click.
;    APPEND:  If set, points are appended to the input arrays,
;             instead of overwriting the old values.
;    VNAME:   Set to a named variable to return an array of tplot variable names,
;             cooresponding to the variables points were chosen from.
;    COLOR:   An alternative color for the crosshairs.  0<=color<=!d.n_colors-1
;    SLEEP:   Sleep time (seconds) between polling the cursor for events.
;             Defaults to 0.1 seconds.  Increasing SLEEP will slow ctime down,
;             but will prevent ctime from monopolizing cpu time.
;    INDS:    Return the indices into the data arrays for the points nearest the
;             recorded times to this named variable.
;    VINDS:   Return the second dimension of the v or y array.
;             Thus  TIME(i) is  data.x(INDS(i))           and
;                   Y(i)    is  data.y(INDS(i),VINDS(i))  and
;                   V(i)    is  data.v(VINDS(i)) or data.v(INDS(i),VINDS(i))
;             for get_data,VNAME(i),data=data,INDS=INDS,VINDS=VINDS
;    EXACT:   Get the time,y, and (if applicable) z values from the data
;             arrays.  If on a multi-line plot, get the value from the line
;             closest to the cursor along y.
;    NOSHOW:  Do not show the plot window.
;    DEBUG:   Avoids default error handling.  Useful for debugging.
;    DAYS, HOURS, MINUTES, SECONDS: Sets time granularity.  For example
;             with MINUTES=1, CTIME will find nearest minute to cursor
;             position.
;PURPOSE:
;   Interactively uses the cursor to select a time (or times)
;NOTES:       If you use the keyword EXACT, ctime may run noticeablly slower.
;         Reduce the number of time you cross panels, especially with
;         tplots of large data sets.
;Mac Os users:
;         You might need to set preferences on your X11 display:
;         Within X11 program go to X11 -> preferences -> Windows: check box labeled "Click=through Inactive Windows"
;         also:                    X11 -> preferences -> Input:  check box labeled "Emulate three button mouse" 
;SEE ALSO:  "crosshairs"
;
;CREATED BY:    Davin Larson & Frank Marcoline
;LAST MODIFICATION:     @(#)ctime.pro   1.44 02/11/01
;WARNING!
;  If ctime crashes, you may need to call:
;  IDL> device,set_graph=3,/cursor_crosshair
;-
pro ctime,time,value,zvalue,$
   fix     = fix,  $         ; use this keyword to reset the graphics curser if ctime has crashed
   append  = append ,$       ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   exact   = exact  ,$       ;
   npoints = npoints,$       ; Keywords for setting ctime mode and
   inds    = inds   ,$       ; for returning data.
   vinds   = inds2  ,$       ;
   panel   = panel  ,$       ;
   ynorm   = ynorm  ,$       ; added by JBT
   xnorm   = xnorm  ,$       ; added by JBT
   plotter_objs = plotter_objs,  $   ; these object(s) are called everytime the curser is updated. (will replace routine_???? keywords
   routine_name = routine_name,  $         ; this routine is called everytime the curser is updated.
   routine_param = routine_param, $
   cut  = cut       ,$                     ; Shortcut to:  ctime,routine_name='tplot_cut'
   counts  = n   ,$     ;
   vname   = vname  ,$       ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   prompt  = prompt ,$       ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   local_time=local_time, $  ;  Displays local time instead of UTC
   psym    = psym   ,$       ;
   silent  = silent ,$       ; Less common keywords for affecting
   noshow  = noshow ,$       ; ctime mode, graphics and text output.
   debug   = debug  ,$       ;
   color   = color  ,$       ;
   sleep   = sleep  ,$       ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   days    = days   ,$       ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   hours   = hours  ,$       ; Keywords for setting time granularity.
   minutes = minutes,$       ;
   seconds = seconds         ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

@tplot_com.pro

if keyword_set(fix) then begin
  device,set_graph=3,/cursor_crosshair
  return
endif

wndw = struct_value(tplot_vars,'settings.window',default=-1)
if wndw eq -1  then begin
    dprint,dlevel=0,'Tplot has not yet been initialized!,  Returning.'
    return
endif
tplot_d = tplot_vars.settings.d
tplot_x = tplot_vars.settings.x
tplot_y = tplot_vars.settings.y
time_scale  = tplot_vars.settings.time_scale
time_offset = tplot_vars.settings.time_offset
tplot_var   = tplot_vars.options.varnames
if keyword_set(cut) then routine_name='tplot_cut'

;;;;;; make sure we have the correct window
flags = 256+128+16+8
if (!d.flags and flags) ne flags then begin
  m='Current device ('+!d.name+') does not support enough features.  See !d.flags'
  dprint,m
  return
endif
if tplot_d.name ne !d.name then begin
  dprint,'Device has changed from "'+tplot_d.name+'" to "'+!d.name+'" since the Last TPLOT.'
  return
endif
current_window = !d.window
wset, tplot_vars.settings.window
if tplot_d.x_size ne !d.x_size or tplot_d.y_size ne !d.y_size then begin
  wset,current_window
  dprint,'TPLOT window has been resized!'
  return
endif
if not keyword_set(noshow) then wshow ;,icon=0 ;open the window
;;;;;;


;;;;;; check keywords and set defaults
if size(/type,sleep) eq 0    then sleep  = 0.01 ;if not set: 0.01, if set 0: 0
if not keyword_set(exact)   then exact  = 0    else exact  = 1
if not keyword_set(npoints) then max    = 2000 else max    = npoints > 1
if not keyword_set(silent)  then silent = 0
if not silent then begin
  if size(/type,prompt) ne 7 then $
    prompt='Use button 1 to select time, 2 to erase time, and 3 to quit.'
  prompt2 = ' point         name:             date/time              yvalue'
  if exact then begin
    strput,prompt2,'comp',21
    prompt2=prompt2+'        (zvalue)'
  end
  print,prompt,prompt2,format='(a)'
endif
;;;;;;

;;;;;; set graphics function, move cursor to screen, and plot original crosshairs
;the graphics function is set to (bitwise) "xor" rather than standard "copy"
;((a xor b) xor b) = a,  lets call your plot "a" and the crosshairs "b"
;plot "a", set the graphics function to "xor", and plot "b" twice and you get "a"
;this way we don't damage your plot
device, get_graphics = old, set_graphics = 6   ;Set xor graphics function
if not keyword_set(color) then color = !d.n_colors-1 $
else color = (color > 0) < (!d.n_colors-1)
px = 0.5                        ;Pointer (cursor) x and y positions
py = 0.5
hx = 0.5                        ;crossHairs       x and y positions
hy = 0.5
;when cursor,x,y,/dev is called and the cursor is off of the plot, (x,y)=(-1,-1)
cursor,testx,testy,/dev,/nowait               ;find current cursor location
if (testx eq -1) and (testy eq -1) then begin ;if cursor not on current window
  tvcrs,px,py,/norm                             ;move cursor to middle of window
endif else begin                              ;cursor is on window.
  pxpypz = convert_coord(testx,testy,/dev,/to_norm) ;find normal coords
  px = pxpypz[0]
  py = pxpypz[1]
  hx = px
  hy = py
endelse
plots,[0,1],[hy,hy], color=color,/norm,/thick,lines=0
plots,[hx,hx],[0,1], color=color,/norm,/thick,lines=0
opx = px                        ;store values for later comparison
opy = py
ohx = hx                        ;store values for later crossHairs deletion
ohy = hy
;if EXACT set, px & py will differ from hx & hy, else they will be the same
;use p{x,y} when working with pointer and h{x,y} when working with crosshairs
;;;;;;


;;;;;; set up output formats
spaces = '                 '    ;wipes out z output from form5 and form6
if !d.name eq 'X' or 1 then begin
  cr = string(13b)                ;a carriage return (no new line)
  form1 = "(4x,a15,': ',6x,a19,x,g10.4,a,a,$)"         ;transient output line
  form2 = "(4x,a15,': [',i2,']  ',a19,x,g10.4,a,a,$)"  ;transient output line, EXACT
  form3 = "(i4,a15,': ',6x,a19,x,g10.4,a)"             ;recorded point output line
  form4 = "(i4,a15,': [',i2,']  ',a19,x,g10.4,a)"      ;recorded point output, EXACT
  form5 = "(4x,a15,': [',i2,']  ',a19,x,g10.4,x,g,a,$)";transient, EXACT, SPEC
  form6 = "(i4,a15,': [',i2,']  ',a19,x,g10.4,x,g)"    ;recorded,  EXACT, SPEC
endif else begin                ;these are for compatibility with MS-Windows
  cr = ''
  form1 = "(4x,a15,': ',6x,a19,x,g,a,a,TL79,$)"    ;same as above six formats
  form2 = "(4x,a15,': (',i2,')  ',a19,x,g,a,a,TL79,$)"
  form3 = "(i4,a15,': ',6x,a19,x,g,a)"
  form4 = "(i4,a15,': (',i2,')  ',a19,x,g,a)"
  form5 = "(4x,a15,': (',i2,')  ',a19,x,g,x,g,a,TL79,$)"
  form6 = "(i4,a15,': (',i2,')  ',a19,x,g,x,g)"
endelse
;;;;;;


;;;;;; get and print initial position and panel in tplot data coordinates
pan = where(py ge tplot_y[*].window[0] and py le tplot_y[*].window[1])
pan = pan[0]
t =  normal_to_data(px,tplot_x) * time_scale + time_offset
if pan ge 0 and pan lt n_elements(tplot_var) then begin
  v =  normal_to_data(py,tplot_y[pan])
  var = tplot_var[pan]
endif else begin
  v =  !values.f_nan
  var = 'Null'
endelse
print,form=form1,var,time_string(t,prec=prec,local_time=local_time),float(v),cr
;;;;;;

;;;;;; create an error handling routine
if not keyword_set(debug) then begin
  catch,myerror
  if myerror ne 0 then begin      ;begin error handler
    plots,[0,1],[ohy,ohy], color=color,/norm,/thick,lines=0 ;erase crosshairs
    plots,[hx,hx],[0,1],   color=color,/norm,/thick,lines=0
    print
    dprint,'Error: ',!error_state.code          ;report problem
    print,!error_state.msg
    tvcrs,0                         ;turn off cursor
    device,set_graphics=old         ;restore old graphics state
    wset,current_window             ;restore old window
    catch,/cancel
    return                          ;exit on error
  endif
endif  ;  else catch,/cancel                       ;end error handler
;;;;;;


;;;;;; set the initial values for internal and output variables
button=0
if not keyword_set(append) then begin
  time = 0
  value = 0
  panel = 0
  vname = ''
  inds = 0
  inds2 = 0
  zvalue = 0
endif
n    =  0
ind  = -1
ind2 = -1
lastvalidvar = var              ;record previous data variable (not 'Null')
oldbutton    = 0                ;record last button pressed
if (exact ne 0) and (var ne 'Null') then $
  ctime_get_exact_data,var,v,t,pan,hx,hy,subvar,ind,ind2,z,$
  spec=spec,dtype=dtype,/load
;;;;;;

;;;;;; here we are:  the main loop...
;------ begin JBT block ----
xnorm = make_array(max, value = !values.f_nan, /float)
ynorm = make_array(max, value = !values.f_nan, /float)
;------ end JBT block ----
print,'Use right button to exit'
while n lt max do begin
  ;the main loop calls cursor,
  ;which waits until there is a button press or cursor movement
  ;the old crosshairs are reploted (erased), the new crosshairs are ploted

  ;;;; get new position, update crosshairs
  cursor,px,py,/change,/norm    ;get the new position
  button = !MOUSE.BUTTON        ;get the new button state
  ;------ begin JBT block ----
  xnorm[n] = px
  ynorm[n] = py
  ;------ end JBT block ----
  hx = px                       ;correct   assignments in the case of EXACT eq 0
  hy = py                       ;temporary assignments in the case of EXACT ne 0
  plots,[0,1],[ohy,ohy], color=color,/norm,/thick,lines=0 ;unplot old cross
  plots,[ohx,ohx],[0,1], color=color,/norm,/thick,lines=0
  if button eq 4 then goto,quit ;yikes! i used a goto!
  if exact eq 0 then begin
    plots,[0,1],[hy,hy], color=color,/norm,/thick,lines=0 ;plot new crosshairs
    plots,[hx,hx],[0,1], color=color,/norm,/thick,lines=0
  endif

  ;;;; Get new data values and crosshair positions from pointer position values,
  ;;;; if we are not deleting the last data point.
  if (opx ne px) or (opy ne py) or (button ne 2) then begin
    t =  normal_to_data(px,tplot_x) * time_scale + time_offset
    res = 1.d/tplot_x.s[1]/!d.x_size
    t = time_round(t,res,days=days,hours=hours,minutes=minutes,seconds=seconds,prec=prec)
    pan = (where(py ge tplot_y[*].window[0] and py le tplot_y[*].window[1]))[0]
    if pan ge 0 and pan lt n_elements(tplot_var) then begin
      v =  normal_to_data(py,tplot_y[pan])
      var = tplot_var[pan]
    endif else begin
      v =  !values.f_nan
      var = 'Null'
    endelse
    ind2 = -1

    if isa(plotter_objs) then begin
      device, set_graphics = old
      wset,current_window
      for i= 0,n_elements(plotter_objs)-1 do begin
        plotter_objs[i].plot,t,var
      endfor
      wset, tplot_vars.settings.window
      device, set_graphics = 6      
    endif

    if keyword_set(routine_param) && isa(routine_param,'dictionary') && isa(routine_param.haskey('routine_name') ) then begin
      routine_name=routine_param.routine_name
    endif

    if size(/type,routine_name) eq 7 then begin
      device, set_graphics = old
      wset,current_window
      if keyword_set(routine_param) then begin
        call_procedure,routine_name,var,t,param=routine_param
      endif else begin
        call_procedure,routine_name,var,t
      endelse
      wset, tplot_vars.settings.window
      device, set_graphics = 6
    endif



    if exact ne 0 then begin
      if var ne 'Null' then begin ;get data points
        load = var ne lastvalidvar
        ctime_get_exact_data,var,v,t,pan,hx,hy,subvar,ind,ind2,z,$
          spec=spec,dtype=dtype,load=load
      endif
      plots,[0,1],[hy,hy], color=color,/norm,/thick,lines=0 ;plot new crosshairs
      plots,[hx,hx],[0,1], color=color,/norm,/thick,lines=0
    endif

    if not silent then begin    ;print the new data
      if keyword_set(subvar) then varn = var+"->"+subvar else varn = var
      tstr = time_string(t,prec=prec,local_time=local_time)
      if ind2 eq -1 then print,form=form1,varn,     tstr,v,spaces,cr $
      else if spec then  print,form=form5,varn,ind2,tstr,v,z     ,cr $
      else               print,form=form2,varn,ind2,tstr,v,spaces,cr
    endif
  endif
  ;;;; got the current data

  ;;;; if a button state changes, take action:
  if button ne oldbutton then begin
    case button of
      1: begin                  ;record the new data and print output line
        append_array,time,t
        append_array,value,v
        if keyword_set(spec  ) then append_array,zvalue,z $
        else                        append_array,zvalue,!values.f_nan
        if keyword_set(subvar) then append_array,vname,subvar $
        else                        append_array,vname,var
        append_array,panel,pan
        np = n_elements(time)
        append_array,inds,ind
        append_array,inds2,ind2 > 0     ;if ind2 eq -1 set to zero
        if not silent then begin
          if ind2 eq -1 then print,form=form3,np-1,varn,     tstr,v,spaces $
          else if spec then  print,form=form6,np-1,varn,ind2,tstr,v,z      $
          else               print,form=form4,np-1,varn,ind2,tstr,v,spaces
        endif
        if keyword_set(psym) then plots,t-time_offset,v,psym = psym
        n = n + 1
      end
      2: begin                  ;delete last data and print output line
        np = n_elements(time)
        if np ge 2 then begin
          time   = time[0:np-2]
          value  = value[0:np-2]
          vname  = vname[0:np-2]
          panel  = panel[0:np-2]
          inds   = inds[0:np-2]
          inds2  = inds2[0:np-2]
          zvalue = zvalue[0:np-2]
          if not silent then $
            print,form="(79x,a,TL79,'last sample (',i0,') deleted.')",cr,np-1
          n = n-1
        endif else if (np ne 0) and (time[0] ne 0) then begin
          time   = 0
          value  = 0
          panel  = 0
          vname  = ''
          inds   = 0
          inds2  = 0
          zvalue = 0
          if not silent then print,$
            form="(79x,a,TL79,'Zero sample (',i0,') set to zero.')",cr,np-1
          n = (n-1) > 0
        end
      end
      else:                     ;do nothing (if 4 then we exited already)
    endcase
  endif

  ;;;; store the current information, and pause (reduce interrupts on cpu)
  if var ne 'Null' then lastvalidvar = var
  oldpanel  = pan
  oldbutton = button
  opx = px
  opy = py
  ohx = hx
  ohy = hy
;  print,'Use right button to exit'
;  print,dwait=5.,dlevel=1,'Use right button to exit'
  wait, sleep                   ;Be nice

endwhile ;;;;;; end main loop

;------ begin JBT block ----
tmp_ind = where(finite(xnorm), n_tmp_ind)
if n_tmp_ind gt 0 then xnorm = xnorm[tmp_ind] else xnorm = !values.f_nan
tmp_ind = where(finite(ynorm), n_tmp_ind)
if n_tmp_ind gt 0 then ynorm = ynorm[tmp_ind] else ynorm = !values.f_nan
;------ end JBT block ----

;;;;;; erase the crosshairs
plots,[0,1],[hy,hy], color=color,/norm,/thick,lines=0
plots,[hx,hx],[0,1], color=color,/norm,/thick,lines=0

;;;;;; return life to normal
quit:
print,cr,format='(79x,a,TL79,$)';clear the line
tvcrs                           ;turn off cursor
device,set_graphics=old         ;restore old graphics state
wset,current_window             ;restore old window

;if not keyword_set(noshow) then wshow

if n_elements(time) eq 1 then begin ;turn outputs into scalars
  time   = time[0]
  value  = value[0]
  panel  = panel[0]
  vname  = vname[0]
  inds   = inds[0]
  inds2  = inds2[0]
  zvalue = zvalue[0]
endif
return
end



