;-----------------------------------------------------------------------------
; NAME: HISTOGRAM
;
; PURPOSE: To read and display PDS HISTOGRAM objects.
;
; CALLING SEQUENCE: Result = HISTOGRAM (structure, numtabs)
;
; INPUTS:
;    Structure: Structure returned by READPDS for the HISTOGRAM file
;    Numtabs: Number of tabs to run HISTOGRAM with
;
; MODIFICATION HISTORY:
;    Written by:    Unknown [<= Jan 22, 2013]
;    Last modified: never
;
;    For a complete list of modifications, see changelog.txt file.
;
;-----------------------------------------------------------------------------

; Notes: 1d & 2d mix for contour plots
; Let user decide contour levels

;------------------------------------------------------------------------------
;- Normal Functions -----------------------------------------------------------
;------------------------------------------------------------------------------

Function All_Tags, structure, rootname

  ; This is a function that recursively searches through
  ; a structure tree, finding ALL of the structure's field names.
  ; It returns a pointer to an array of pointers, each pointing
  ; to the names of structure fields.

  IF N_Elements(rootname) EQ 0 THEN rootname = '.' ELSE $
    rootname = StrUpCase(rootname) + '.'
  names = Tag_Names(structure)
  retValue = Ptr_New(rootname + names)

  ; If any of the fields are structures, report them too.

  FOR j=0,N_Elements(names)-1 DO BEGIN
    ok = Execute('s = Size(structure.' + names[j] + ')')
    IF s[s[0]+1] EQ 8 THEN BEGIN
      newrootname = rootname + names[j]
      theseNames = Call_Function('All_Tags', $
        structure.(j), newrootname)
      retValue = [[retValue],[theseNames]]
    ENDIF
  ENDFOR

  RETURN, retValue
END
;-------------------------------------------------------------------



function get_tags, structure, rootname

  ; This function returns the names of all structure fields
  ; in the structure as a string array. The names are given
  ; as valid structure names from the root structure name,
  ; which can be passed in along with the structure itself.

  On_Error, 1

  ; Check parameters.

  CASE N_Params() OF

    0: BEGIN
      Message, 'Structure argument is required.'
    ENDCASE

    1: BEGIN
      rootname = ''
      s = Size(structure)
      IF s[s[0]+1] NE 8 THEN $
        Message, 'Structure argument is required.'
    ENDCASE

    2: BEGIN
      s = Size(structure)
      IF s[s[0]+1] NE 8 THEN $
        Message, 'Structure argument is required.'
      s = Size(rootname)
      IF s[s[0]+1] NE 7 THEN $
        Message, 'Root Name parameter must be a STRING'
    ENDCASE

  ENDCASE

  tags = All_Tags(structure, rootname)

  ; Extract and free the first pointer.

  retval = [*tags[0,0]]
  Ptr_Free, tags[0,0]

  ; Extract and free the the rest of the pointers.

  s = Size(tags)
  FOR j=1,s[2]-1 DO BEGIN
    retval = [retval, *tags[0,j]]
    Ptr_Free, tags[0,j]
  ENDFOR
  Ptr_Free, tags

  ; Return the structure names.

  return, retval
END

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Description   : Removes a tag from a structure                      ;;;
;;; Pre-Condition : Must enter a valid structure and tag name           ;;;
;;; Post-Condition: Returns the structure without the supplied tag name ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function rm_tag, input, badtag
   all_tags = get_tags(input)                                     ; Get all the tags for the input structure
   top_tags = strarr(n_elements(all_tags))                        ; Findthe parent tag name (since all_tags may return badtag.XXX)

   for z=0,n_elements(all_tags)-1,1 do begin
      spltarr = strsplit(all_tags[z],'.',/extract)
      top_tags[z] = spltarr[0]
   endfor

   bad      = [strupcase(badtag),"."+strupcase(badtag)]           ; Store the possible bad tag structures

   tmpstr   = input                                               ; Create a temporary copy of the structure
   goodind  = where(top_tags NE bad[0] AND all_tags NE bad[1])    ; Find where the GOOD tags are
   newtags  = all_tags[goodind]                                   ; Save a list of all the good tags

   exstr = "output = create_struct("
   for a=0,n_elements(newtags)-1,1 do begin
      exstr = exstr + '"'+strmid(newtags[a],1)+'",tmpstr'+newtags[a]+','
   endfor
   exstr = strmid(exstr,0,strlen(exstr)-1)+")"

print,exstr

   void = EXECUTE(exstr)
   return,output
end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Description   : Creates a 2D Array containing input to CONTOUR               ;;;
;;; Pre-Condition : A cache variable that contains the substructure "indices"    ;;;
;;;                 "indices" is a 2xN array with the (X,Y) coordinates of every ;;;
;;;                 selected item in the table. Used for testing if current tab  ;;;
;;;                 has selected values. This is a bit of a hold over from a     ;;;
;;;                 previous version.                                            ;;;
;;; Post-Condition: Returns a 2D Array with table values to go into CONTOUR.     ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

function makecon, cache, itr,shiftx,shifty
   indtab = "IND"+strtrim(string(itr),1)
   stat   = tag_exist(cache.indices,indtab)           ; Return Boolean value on whether cache.indices exists

   IF stat EQ 1 THEN BEGIN
      WIDGET_CONTROL, cache.tablelist[itr], GET_VALUE=value, /USE_TABLE_SELECT   ; Get table value for current coordinate
      
      indarr = WIDGET_INFO(cache.tablelist[itr], /TABLE_SELECT)

      xmin   = min(indarr[0,*])
      ymin   = min(indarr[1,*])
      xdim   = max(indarr[0,*])-min(indarr[0,*])+1    ; Find the X-Dimensions of contour array by taking span of selected indices
      ydim   = max(indarr[1,*])-min(indarr[1,*])+1    ; Find the Y-Dimensions of contour array by taking range of selected indices
      
      xdim   = xdim + shiftx
      ydim   = ydim + shifty

      output = fltarr(xdim,ydim)

      cache.conoff[*,itr] = [xmin,ymin]

      for a=0L,n_elements(indarr[0,*])-1,1 do begin
         ix = indarr[0,a] - xmin                      ; Shift the x index to fit within range of new table
         iy = indarr[1,a] - ymin                      ; Shift the y index to fit within range of new table

         output[ix+shiftx,iy+shifty] = value[a]
      endfor

      return,output
   ENDIF ELSE BEGIN
      return,-1
   ENDELSE
end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Description   : Creates string array of axis values to display              ;;;
;;; Pre-Condition : Requires the cache variable. The user must submit a valid   ;;;
;;;                 binsize, and there must be a selection made in the table.   ;;;
;;; Post-Condition: Returns either a 1D String Array with Axis values if the    ;;;
;;;                 user has opted for a Histogram, or a 2D string array if the ;;;
;;;                 user opted for a contour plot.                              ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function xvals, cache
   IF cache.disptype EQ "histogram" THEN BEGIN                    ; If cache.disptype is set to histogram, create 1D string array of x values
      FOR i=0,n_elements(cache.tablelist)-1,1 DO BEGIN            ; Loop over all tables
         tabs   = get_tags(cache.indices)                         ; Store tabs with selected values
         tabitr = intarr(n_elements(tabs))                        ; Integer array that will store which tabs are in use so we only access corresponding tables
   
         for a=0,n_elements(tabs)-1,1 do begin
            tabitr[a] = int(strmid(tabs[a],4))                    ; Strip the number from a tab name to populate tabitr
         endfor
   
         goodtabs = where(i EQ tabitr)                            ; Test to see if the current table is in use

         IF goodtabs[0] NE -1 THEN BEGIN
            WIDGET_CONTROL, cache.tablelist[i], GET_VALUE=value, /USE_TABLE_SELECT   ; Get the currently selected table values and store to 'value'
         
            IF i EQ int(tabitr[0]) THEN BEGIN                  ; Need to initialize minm and maxm
               minm = min(value)
               maxm = max(value)
            ENDIF ELSE BEGIN
               IF min(value) LT minm THEN minm = min(value)    ; If the current value is less than the original minm, save it
               IF max(value) GT maxm THEN maxm = max(value)    ; If the current value is greater than the original maxm, save it
            ENDELSE
         ENDIF
      ENDFOR

      WIDGET_CONTROL, cache.binsize, GET_VALUE=bin             ; Get the current number of bins the user wants

      dx   = float(bin[0])
      nbin = int(((maxm-minm)/dx)+1)
      nbin = nbin[0]
      xvector = (nbin LT 5) ? strarr(5) : strarr(nbin)

     
      for i=0L,nbin-1,1 do begin
         xvector[i] = strtrim(string(minm+i*dx),1)
      endfor
   
      if nbin LT 5 then begin
         dbin = 5-nbin
         for a=0,dbin-1,1 do begin
            xvector[n_elements(xvector)-1-a] = ' '
         endfor
      endif

      return, xvector
   ENDIF ELSE BEGIN                                         ; If cache.disptype is not 'histogram' then it must be 'distribution'
      tags = get_tags(cache.indices)                        ; Store all tag names currently in cache.indices
      for k=0,n_elements(tags)-1,1 do begin
         exstr = 'tempind = cache.indices'+tags[k]
         void  = EXECUTE(exstr)                             ; Save the current tag in cache.indices to tempind
            
         IF k EQ 0 THEN indarr = tempind                    ; Initialize indarr from tempind
         IF k NE 0 THEN indarr = [[indarr],[tempind]]       ; Concatenate indarr with tempind
      endfor
      
      xvector = strarr(2,6)
      
      minx = min(indarr[0,*])
      maxx = max(indarr[0,*])
      miny = min(indarr[1,*])
      maxy = max(indarr[1,*])
      
      dx = (maxx-minx)/5.
      dy = (maxy-miny)/5.
      
      for a=0,5,1 do begin
         xvector[0,a] = strtrim(string(minx+a*dx),1)
         xvector[1,a] = strtrim(string(miny+a*dy),1)
      endfor
      return,xvector
   ENDELSE
end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Description   : Finds the dimensions of an input array of up to two dimensions ;;;
;;; Pre-Condition : An array.                                                      ;;;
;;; Post-Condition: Returns a structure with the X-dimensions and Y-dimensions.    ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

function dimensions,iTable
   sz = size(iTable,/DIMENSIONS)
   sx = sz[0]
   sy = (n_elements(sz) EQ 1) ? 0 : sz[1]
   return,{sx:sx,sy:sy}
end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Description   : Produces the vector containing the histogram output      ;;;
;;; Pre-Condition : Requires the cache variable to be passed as an argument. ;;;
;;; Post-Condition: Returns a vector with histogram values.                  ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function makehist, cache, itr
   WIDGET_CONTROL, cache.binsize, GET_VALUE=bin

   tabs   = get_tags(cache.indices)
   tabitr = intarr(n_elements(tabs))
   
   for a=0,n_elements(tabs)-1,1 do begin
      tabitr[a] = float(strmid(tabs[a],4))
   endfor
   
   goodtabs = where(itr EQ tabitr)

   IF goodtabs[0] NE -1 THEN BEGIN
      WIDGET_CONTROL, cache.tablelist[itr], GET_VALUE=value, /USE_TABLE_SELECT
   ENDIF ELSE BEGIN
      return,-1
   ENDELSE

   value = DOUBLE(value)

   if cache.histmins[0] EQ -999 then begin
      cache.histmins = [min(value)]
   endif else begin
      tmp   = [cache.histmins,min(value)]
      cache = rm_tag(cache,"histmins")
      cache = create_struct(cache,histmins,tmp)
   endelse

   return,histogram(value,BINSIZE=bin)
end


;- Environment Functions -----------------------------------------------------

function helpinfo, ev
   IF !VERSION.OS EQ 'linux' THEN spawn,'nice +19 nedit histreadme.txt'
   IF !VERSION.OS EQ 'MacOS' THEN spawn,'textedit histreadme.txt'
end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Description   : Changes the display type variable in the cache.          ;;;
;;; Pre-Condition : Requires the ev structure to be passed from the xmanager ;;;
;;; Post-Condition: Returns a modified cache structure by means of ev.TOP    ;;;
;;; NOTE          : Logically identical to disptype_hist, but is a seperate  ;;;
;;;                 function due to simplicity of widget use. UVALUE could   ;;;
;;;                 potentially allow for a merger of these two functions.   ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function disptype_dist, ev
   WIDGET_CONTROL, ev.TOP, GET_UVALUE=cache
   
   cache.disptype = "distribution"

   WIDGET_CONTROL, ev.TOP, SET_UVALUE=cache
end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Description   : Changes the display type variable in the cache.          ;;;
;;; Pre-Condition : Requires the ev structure to be passed from the xmanager ;;;
;;; Post-Condition: Returns a modified cache structure by means of ev.TOP    ;;;
;;; NOTE          : Logically identical to disptype_hist, but is a seperate  ;;;
;;;                 function due to simplicity of widget use. UVALUE could   ;;;
;;;                 potentially allow for a merger of these two functions.   ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function disptype_hist, ev
   WIDGET_CONTROL, ev.TOP, GET_UVALUE=cache

   cache.disptype = "histogram"

   WIDGET_CONTROL, ev.TOP, SET_UVALUE=cache
end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Description   : Actually selects the table values passed by the users      ;;;
;;;                 clicks or manual selection. A user MUST call this function ;;;
;;;                 if they wish to create/change their values.                ;;;
;;; Pre-Condition : Requires the ev structure to be passed from the xmanager   ;;;
;;; Post-Condition: Returns a modified cache structure by means of ev.TOP. It  ;;;
;;;                 also highlights the current selection in the table. This   ;;;
;;;                 is important, as other functions grab highlighted portions ;;;
;;;                 directly.                                                  ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function highlight, ev
   WIDGET_CONTROL, ev.TOP, GET_UVALUE=cache                          ; Read in the cache
   WIDGET_CONTROL, cache.coord1, GET_VALUE=coord1                    ; Read in the (x0,y0) coordinates
   WIDGET_CONTROL, cache.coord2, GET_VALUE=coord2                    ; Read in the (x1,y1) coordinates

   itab = WIDGET_INFO(cache.Tab,/TAB_CURRENT)                        ; Determine the current tab
   
   WIDGET_CONTROL, cache.tablelist[itab], GET_VALUE=iTable

   IF coord1 EQ "(x0,y0): " AND coord2 EQ "(x1,y1): " THEN BEGIN     ; If the user chose to select from the table, save the selections
      ind = WIDGET_INFO(cache.tablelist[itab], /TABLE_SELECT)
   ENDIF ELSE IF coord1 NE "(x0,y0): " AND coord2 NE "(x1,y1): " THEN BEGIN
      ;;; Find the x0 and y0 coordinates
      cstr = strmid(coord1,10)
      carr = strsplit(cstr,',',/extract)
      yarr = strsplit(carr[1],')',/extract)
      x0   = int(carr[0])
      y0   = int(yarr[0])

      ;;; Find the x1 and y1 coordinates
      cstr = strmid(coord2,10)
      carr = strsplit(cstr,',',/extract)
      yarr = strsplit(carr[1],')',/extract)
      x1   = int(carr[0])
      y1   = int(yarr[0])

      xp   = [x0,x1]
      yp   = [y0,y1]
      ymin = min(yp,minind)
      ymax = max(yp,maxind)
      xmin = xp[minind]
      xmax = xp[maxind]
   
      dx   = (abs(x1-x0) GT 0) ? abs(x1-x0) : 1                      ; Find the x dimension of selection. If 1D vector, force to 1.
      dy   = (abs(y1-y0) GT 0) ? abs(y1-y0) : 1                      ; Find the y dimension of selection. If 1D vector, force to 1.
            
      slope = float((ymax-ymin))/float((xmax-xmin))
      
      if abs(slope) GE 1 then begin
         slope = (slope LT 0) ? ceil(slope) : floor(slope)
         dy    = abs(slope)*dx
         ind   = intarr(2,dy)

         for j=0,dy-1,1 do ind[1,j] = ymin+j

         pos = 0
         for k=0,dx-1,1 do begin
            for l=0,abs(slope)-1,1 do begin
               ind[0,pos] = (slope GT 0) ? xmin+k:xmin-k
               pos=pos+1
            endfor
         endfor
      endif else begin
         slope = (slope LT 0) ? ceil(1/slope) : floor(1/slope)
         dx    = abs(slope)*dy
         ind   = intarr(2,dx)
         
         if slope GT 0 then begin
            for j=0,dx-1,1 do ind[0,j] = xmin+j
         endif else begin
            for j=0,dx-1,1 do ind[0,j] = xmin-j
         endelse
         
         pos = 0
         for k=0,dy-1,1 do begin
            for l=0,abs(slope)-1,1 do begin
               ind[1,pos] = ymin+k
               pos = pos +1
            endfor
         endfor
      endelse
   ENDIF ELSE BEGIN
      return,-1
   ENDELSE


   ;;; Remove infs and NaNs from the selection. If they are all
   ;;; Infs or NaNs, then select nothing.
   if ind[0] NE -1 then begin
      WIDGET_CONTROL, cache.tablelist[itab], GET_VALUE=vals,USE_TABLE_SELECT=ind

      infs = where(finite(vals) EQ 1)
      if infs[0] EQ -1 then return,-1
   
      indtmp = intarr(2,n_elements(infs))

      for idx=0L,n_elements(infs)-1,1 do begin
         indtmp[*,idx] = ind[*,infs[idx]]
      endfor
      
      ind = indtmp
   endif

   tabstr = 'ind'+strtrim(string(itab),1)                            ; "ind"+ the current tab number is so we can have a tag name that we can break up for info later
   stat   = tag_exist(cache,"indices")                               ; Determine whether the indices structure has been made or not
   
   IF stat EQ 0 THEN BEGIN
      IF ind[0] NE -1 THEN BEGIN                                     ; If indices does not exist, but we have new data to enter, then insert indices and the ind#
         tempind = create_struct(tabstr,ind)
         cache   = create_struct(cache,"indices",tempind)
      ENDIF
   ENDIF ELSE BEGIN                                                  ; Logic for situations where indices already exists
      all_tags = get_tags(cache.indices)
      stat_sub = tag_exist(cache.indices,tabstr)

      IF ind[0] NE -1 THEN BEGIN
         IF stat_sub EQ 0 THEN BEGIN                                 ; If our current tab has not been added to cache.indices, add it
            cachetmp = create_struct(cache.indices,tabstr,ind)       ; Create a temp structure with the old cache.indices concatenated with the new ind#
            cache    = rm_tag(cache,"indices")                       ; Remove cache.indices
            cache    = create_struct(cache,"indices",cachetmp)       ; Recreate cache.indices with the temp structure (to avoid IDL errors of directly over-writing)
         ENDIF
         IF stat_sub EQ 1 THEN BEGIN                                 ; If our current tab has been added to cache.indices, modify it
            IF n_elements(all_tags) GT 1 THEN BEGIN
               cachetmp      = rm_tag(cache.indices,tabstr)
               cache         = rm_tag(cache,"indices")
               cache         = create_struct(cache,"indices",create_struct(cachetmp,tabstr,ind))
            ENDIF ELSE BEGIN
               cachetmp = create_struct(tabstr,ind)
               cache    = rm_tag(cache,"indices")
               cache    = create_struct(cache,"indices",cachetmp)
            ENDELSE
         ENDIF
      ENDIF
      
      IF ind[0] EQ -1 && stat_sub EQ 1 THEN BEGIN                    ; If our current tab exists, but nothing is selected, remove that tag
         IF n_elements(all_tags) EQ 1 THEN BEGIN                     ; If our current tab exists and the only one, remove cache.indices
            cache = rm_tag(cache,"indices")
         ENDIF ELSE BEGIN
            cachetmp = rm_tag(cache.indices,tabstr)
            cache    = rm_tag(cache,"indices")
            cache    = create_struct(cache,"indices",cachetmp)
         ENDELSE
      ENDIF
   ENDELSE

   WIDGET_CONTROL, ev.TOP, SET_UVALUE=cache                          ; Save the new cache to ev.TOP 
   WIDGET_CONTROL, cache.tablelist[itab], SET_TABLE_SELECT=ind       ; Set the selection in the table
end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Description   : Display TVSCL of current table data                      ;;;
;;; Pre-Condition : Requires the ev structure to be passed from the xmanager ;;;
;;; Post-Condition: Returns a new copy of cache via ev.TOP                   ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function tvscl, ev
   WIDGET_CONTROL, ev.TOP, GET_UVALUE=cache

   itab = WIDGET_INFO(cache.Tab,/TAB_CURRENT)                  ; Store the current tab number

   WIDGET_CONTROL, cache.tablelist[itab], GET_VALUE=value      ; Grab all values from the table
   
   cache.plottype = 1                                          ; Set the display type to 1 so zoom knows what it is handling

   WSET, cache.preview                                         ; Set the display destination to the draw widget
   DEVICE, DECOMPOSED=1
   TVSCL, congrid(value,300,300)                               ; Display the table data fitted to be 300x300 pixels
   DEVICE, DECOMPOSED=0
   
   WIDGET_CONTROL, ev.TOP, SET_UVALUE=cache                    ; Store the new cache to ev.TOP
end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Description   : Plots the histogram or contour plots to the main draw widget. ;;;
;;; Pre-Condition : Requires the ev structure to be passed from the xmanager      ;;;
;;; Post-Condition: Returns a new copy of cache via ev.TOP                        ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function plothist, ev
   WIDGET_CONTROL, ev.TOP, GET_UVALUE=cache
   WIDGET_CONTROL, cache.binsize, GET_VALUE=binval
   
   flag = 0                                                             ; flag lets the code know if this is the first result actually plotted or not
   stat = tag_exist(cache,"indices")                                    ; Boolean test whether indices already exists or not.

   indcnt = (stat EQ 1) ? n_elements(get_tags(cache.indices)):0         ; Find number of tabs with selections, allows us to vary color of plots  
   cache.plottype = 2                                                   ; Set plottype to 2, which lets the code know we are NOT using TVSCL
   WSET,cache.preview                                                   ; Set the display window to the main draw widget

   IF cache.disptype EQ "histogram" AND stat EQ 1 THEN BEGIN            ; If there is some selection active, and we're plotting histograms, begin
      FOR j=0,n_elements(cache.tablelist)-1,1 DO BEGIN                  ; Loop over all possible tables
         output = makehist(cache,j)                                     ; Save the output from makehist to plot to screen
  
         IF output[0] EQ -1 THEN continue                               ; makehist will return -1 if the current iteration of tables has no selection

         xval   = xvals(cache)                                          ; Get the axis labels and store to string array
     
         IF flag EQ 0 THEN BEGIN
            IF n_elements(xval) GT 8 THEN BEGIN                         ; If xvals is more than 8 indicies, reduce it to 8 to avoid clutter
               dx  = int((n_elements(xval)-1)/8.)
               tmp = [xval[0]]
               for a=1,7,1 do tmp = [tmp,xval[0+a*dx]]
               xval = tmp
            ENDIF

            numtick = n_elements(xval)-1
            plot,output,psym=10,xtickname=xval,xticks=numtick
            flag = 1
         ENDIF ELSE BEGIN
            oplot,output,psym=10,color=(j*250/indcnt)
         ENDELSE
      ENDFOR
   ENDIF
   IF cache.disptype EQ "distribution" AND stat EQ 1 THEN BEGIN               ; If there is some selection active, and we're plotting contours, begin
      FOR j=0,n_elements(cache.tablelist)-1,1 DO BEGIN                        ; Loop over all possible tables
         output = makecon(cache,j,0,0)                                        ; Save the output from makecon to plot to screen

         IF output[0] EQ -1 THEN cache.conoff[*,j] = [-1,-1]
      ENDFOR

      FOR j=0,n_elements(cache.tablelist)-1,1 DO BEGIN
         IF cache.conoff[0,j] NE -1 && cache.conoff[1,j] NE -1 THEN BEGIN
            output = makecon(cache,j,0,0)
            sz     = size(output)                                              ; If output is 1D, then we just plot it, if 2D, then we use contour

            if sz[0] EQ 1 then begin
               IF flag EQ 0 THEN BEGIN
                  plot,output,XTITLE='Column'
                  flag = 1
               ENDIF ELSE BEGIN
                  oplot,output,color=(j*250/indcnt)
               ENDELSE
            endif else begin
               xval   = xvals(cache)

               difx   = cache.conoff[0,j] - max([min(cache.conoff[0,*]),0])
               dify   = cache.conoff[1,j] - max([min(cache.conoff[1,*]),0])

               output=makecon(cache,j,difx,dify)


               IF flag EQ 0 THEN BEGIN
                  contour,output,xtickname=xval[0,*],ytickname=xval[1,*],XTITLE='Column',YTITLE='Row',NLEVELS=5
                  flag = 1
               ENDIF ELSE BEGIN
                  contour,output,color=(j*250/indcnt),/OVERPLOT,NLEVELS=5
               ENDELSE
            endelse
         ENDIF
      ENDFOR
   ENDIF

   X = [-2,0,2]
   y = [0,0,0]
   usersym,x,y
   
   legname = strarr(indcnt)
   legsym  = intarr(indcnt)+8
   col     = intarr(indcnt)
   indname = get_tags(cache.indices)
   
   for a=0,indcnt-1,1 do begin
      numstr     = strmid(indname[a],4)
      legname[a] = 'Tab '+numstr
      col[a]     = (a*250/indcnt)
   endfor
   
   col[0]=250
   
   IF cache.plottype NE 1 THEN BEGIN
      legend,legname,psym=legsym,colors=col
   ENDIF

   IF cache.disptype EQ "histogram" AND cache.plottype NE 1 THEN BEGIN
      INFOSTR = "Minimum is: "+strtrim(string(min(cache.histmins)),2)
      xyouts,75,60,INFOSTR,/DEVICE
            
      INFOSTR = "Binsize is: "+strtrim(string(cache.binsize[0]),2)
      xyouts,75,50,INFOSTR,/DEVICE
      
      cache.histmins = [float(-999)]
   ENDIF

   WIDGET_CONTROL, ev.TOP, SET_UVALUE=cache
end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Description   : Takes the user selected tag from a PDS structure, and       ;;;
;;;                 the current table values with the newly selected data.      ;;;
;;; Pre-Condition : Requires the ev structure to be passed from the xmanager    ;;;
;;; Post-Condition: Returns a new copy of cache via ev.TOP and replaces content ;;;
;;;                 of the main widgets table.                                  ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function newform, ev
   if ev.CLICKS EQ 2 then begin                                                     ; We only want to replace values on a double click
      WIDGET_CONTROL, ev.TOP, GET_UVALUE=cache                                      ; Get the current cache from ev.TOP
      
      for i=0,n_elements(cache.leafid)-1,1 do begin                                 ; Loop over all leaves
         flag = WIDGET_INFO(cache.leafid[i], /TREE_SELECT)                          ; Return boolean if the current leaf in the tree is selected or not
         if flag EQ 1 then iLeaf = i                                                ; Save the current indice for later referencing
      endfor

      itab = WIDGET_INFO(cache.Tab,/TAB_CURRENT)

      WIDGET_CONTROL, cache.leafid[iLeaf], GET_UVALUE=iObj                          ; Get the User Value for selected leaf (The substructure path of given PDS structure)
      WIDGET_CONTROL, cache.tablelist[itab], GET_UVALUE=pdsf, GET_VALUE=iTable      ; Get the User Value for selected leaf (The PDS structure passed by readpds)
      
      exstr = 'newtable = pdsf'+iObj
      void  = EXECUTE(exstr)                                                        ; Store the array from PDS structure to newtable so we can display it

      szstr = dimensions(iTable)                                                    ; Get the current table dimensions
      sx    = szstr.sx
      sy    = szstr.sy
      
      szstr = dimensions(newtable)                                                  ; Get the new table dimensions
      newsx = szstr.sx
      newsy = szstr.sy
            
      dx = (newsx-sx)                                                               ; Find x size difference of old and new
      dy = (newsy-sy)                                                               ; Find y size difference of old and new

      if (dx GT 0) then begin
         WIDGET_CONTROL, cache.tablelist[itab], INSERT_COLUMNS=dx                   ; If the new table is wider, insert columns so we can display everything
      endif
      if (dy GT 0) then begin
         WIDGET_CONTROL, cache.tablelist[itab], INSERT_ROWS=dy                      ; If the new table is taller, insert columns so we can display everything
      endif
      
      WIDGET_CONTROL, cache.tablelist[itab], SET_VALUE=[newtable]                   ; Replace old table with new table
   endif
end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Description   : Saves the current selection from all active tabs.        ;;;
;;; Pre-Condition : Requires the ev structure to be passed from the xmanager ;;;
;;; Post-Condition: Saves a file to current folder called 'histogram.save'   ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function save, ev
   WIDGET_CONTROL, ev.TOP, GET_UVALUE=cache                                               ; Grab the cache
   
   flag = 0                                                                               ; Flag to differentiate initialization from concatenation
   stat = tag_exist(cache,"indices")                                                      ; Check to see if any selections have been made

   FOR j=0,n_elements(cache.tablelist)-1,1 DO BEGIN
      tabstr  = 'ind'+strtrim(string(j),1)                                                ; Form substring needed to get info from cache.indices for current tab
      
      if stat EQ 1 then begin                                                             ; If there is a selection on this tab, proceed
         substat = tag_exist(cache.indices,tabstr)
         
         if substat EQ 1 then begin
            exstr = 'tmp = cache.indices.ind'+strtrim(string(j),1)                        ; Save the indices from that tab to the variable tmp
            void  = EXECUTE(exstr)
            
            WIDGET_CONTROL, cache.tablelist[j],SET_TABLE_SELECT=tmp                       ; Use those indices to highlight correct values in that tab
            WIDGET_CONTROL, cache.tablelist[j],GET_VALUE=vals,/USE_TABLE_SELECT           ; Grab the highlighted values
            
            if flag EQ 0 then begin
               output = create_struct("tab"+strtrim(string(j),1),vals)                    ; Initialize a structure to store final results
               flag   = 1                                                                 ; trip the flag
            endif else begin
               output = create_struct(output,"tab"+strtrim(string(j),1),vals)             ; Concatenate new values into the structure
            endelse
         endif
      endif
   endfor
   save,output,FILENAME='histogram.save'                                                  ; Save our structure as histogram.save
end         
   
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Description   : Exits the current session.                               ;;;
;;; Pre-Condition : Requires the ev structure to be passed from the xmanager ;;;
;;; Post-Condition: None.                                                    ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function exit, ev
   WIDGET_CONTROL, ev.TOP, /DESTROY
end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Description   : Event function for the zoomed window. Allows the user to    ;;;
;;;                 either close the window with a left double-click, or to     ;;;
;;;                 choose new selection points with single right-click.        ;;;
;;; Pre-Condition : Requires the ev structure to be passed from the xmanager    ;;;
;;; Post-Condition: If new points are selected, it will replace the coordinates ;;;
;;;                 in the main widget.                                         ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pro zoomed_event, ev
   WIDGET_CONTROL, ev.TOP, GET_UVALUE=cache                                               ; Grab the cache

   if ev.CLICKS EQ 2 THEN BEGIN                                                           ; If the user double clicks, close the zoomed window
      WIDGET_CONTROL, ev.TOP, /DESTROY
   endif
   
   IF ev.CLICKS EQ 1 AND ev.PRESS EQ 4 AND cache.plottype EQ 1 then begin                 ; On single right click, store user defined coordinate for draw selection
      WIDGET_CONTROL, cache.coord1, GET_VALUE=coord1 
      WIDGET_CONTROL, cache.coord2, GET_VALUE=coord2
      WIDGET_CONTROL, cache.zoom, GET_VALUE=zoom

      itab = WIDGET_INFO(cache.Tab,/TAB_CURRENT)
      WIDGET_CONTROL, cache.tablelist[itab], GET_VALUE=iTable
         
      sz   = dimensions(iTable)
      xdim = sz.sx
      ydim = sz.sy
         
      xfac = xdim/float((300*zoom[0]))
      yfac = ydim/float((300*zoom[0]))

      xpos = strtrim(string(int(ev.x*xfac)),1)
      ypos = strtrim(string(int(ev.y*yfac)),1)

      IF coord1 EQ "(x0,y0): " THEN BEGIN
         WIDGET_CONTROL, cache.coord1, SET_VALUE='(x0,y0): ('+xpos+','+ypos+')'
      ENDIF
      
      IF coord1 NE "(x0,y0): " AND coord2 EQ "(x1,y1): " THEN BEGIN
         WIDGET_CONTROL, cache.coord2, SET_VALUE='(x1,y1): ('+xpos+','+ypos+')'
      ENDIF
      
      IF coord1 NE "(x0,y0): " AND coord2 NE "(x1,y1): " THEN BEGIN
         WIDGET_CONTROL, cache.coord1, SET_VALUE='(x0,y0): '
         WIDGET_CONTROL, cache.coord2, SET_VALUE='(x1,y1): '
      ENDIF
   ENDIF
end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Description   : Event function for the small window. Allows the user to        ;;;
;;;                 either zoom the window with a left double-click, or to         ;;;
;;;                 choose new selection points with single right-click.           ;;;
;;; Pre-Condition : Requires the ev structure to be passed from the xmanager       ;;;
;;; Post-Condition: If new points are selected, it will replace the coordinates    ;;;
;;;                 in the main widget. If they choose to zoom, it spawns a larger ;;;
;;;                 draw widget.                                                   ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pro histogram_event, ev
   WIDGET_CONTROL, ev.TOP, GET_UVALUE=cache                                                  ; Grab the cache
   itab = WIDGET_INFO(cache.Tab,/TAB_CURRENT)                                                ; Get the current tab so we can reference the correct values
   flag = 1
   stat = tag_exist(ev,"clicks")                                                             ; Check to see if the user actually clicked on the widget (Leftover failsafe)

   IF cache.plottype EQ 0 THEN BEGIN
      flag = 0
   ENDIF

   IF stat EQ 1 THEN BEGIN
      IF ev.CLICKS EQ 2 AND ev.PRESS EQ 1 AND flag EQ 1 THEN BEGIN                           ; On double left click when the draw widget isnt empty, zoom 
         WIDGET_CONTROL, cache.zoom, GET_VALUE=zoom                                          ; Grab the user specified zoom value
         
         zoom   = int(zoom)                                                                  ; Convert string to int
         width  = zoom*300                                                                   ; New width value
         height = zoom*300                                                                   ; New height value
   
         LRGmain     = WIDGET_BASE(/COLUMN)                                                  ; Initialize a new base to draw in
         LRG_preview = WIDGET_DRAW(LRGmain,/BUTTON_EVENT,XS=width,YS=height,RETAIN=2)        ; Initialize the draw widget for the new scaled plot
         
         WIDGET_CONTROL, LRGmain, /realize
         WIDGET_CONTROL, LRG_preview, GET_VALUE=LRGdrawID                                    ; Grab the widget ID for the new draw wiget
         
         cache = create_struct(cache,"LRG_preview",LRGdrawID)                                ; Stuff out new draw widget id into the cache
         WIDGET_CONTROL,LRGmain,SET_UVALUE=cache                                             ; Store the new cache variable in our widget base
         
         WSET, LRGdrawID
         
         IF cache.plottype EQ 1 THEN BEGIN                                                   ; If we are working with the tvscl output, we need to grab the right values
            WIDGET_CONTROL, cache.tablelist[itab], GET_VALUE=fulltable                       ; Get the values for the current tab
            DEVICE, DECOMPOSED=1                                                             ; Allow us to edit draw widget for a moment
            TVSCL,congrid(fulltable,width,height)                                            ; Show the current tab values when rescaled to the zoomed size
            DEVICE, DECOMPOSED=0                                                             ; Prevent toying around with the widget
         ENDIF

         indcnt = (tag_exist(cache,"indices") EQ 1) ? n_elements(get_tags(cache.indices)):0  ; Find out how many tabs are actually being used
         
         IF cache.plottype EQ 2 THEN BEGIN                                                   ; 2 if a histogram or contour plot (Leftover from alpha. Should merge disptype
                                                                                             ; and plottype properly.)
            flag = 0                                                                         ; flag lets the code know if this is the first result actually plotted or not
            IF cache.disptype EQ "histogram" THEN BEGIN
               FOR j=0,n_elements(cache.tablelist)-1,1 DO BEGIN                              ; Loop over the different tables to find any data that needs plotting
                  output = makehist(cache,j)                                                 ; Create a histogram using makehist from the current table

                  IF output[0] NE -1 THEN BEGIN
                     xval   = xvals(cache)                                                   ; Get string array with the bin locations

                     IF flag EQ 0 THEN BEGIN
                        IF n_elements(xval) GT 8 THEN BEGIN                                  ; If xvals is more than 8 indicies, reduce it to 8 to avoid clutter
                           dx  = int((n_elements(xval)-1)/8.)
                           tmp = [xval[0]]
                           for a=1,7,1 do tmp = [tmp,xval[0+a*dx]]
                           xval = tmp
                        ENDIF
                           
                        numtick = n_elements(xval)-1                                         ; Get the number of ticks we'll need to set the plot to display
                        plot,output,PSYM=10,xtickname=xval,xticks=numtick                    ; Plot the histogram with our custom labels
                        flag = 1                                                             ; trip the flag so any other histograms will use overplot instead
                     ENDIF ELSE BEGIN
                        oplot,output,psym=10,color=(j*250/indcnt)                            ; Overplot any additional histograms using a different color
                     ENDELSE
                  ENDIF
               ENDFOR
            ENDIF ELSE BEGIN
               FOR j=0,n_elements(cache.tablelist)-1,1 DO BEGIN                              ; Loop over the different tables to find any data that needs plotting
                  output = makecon(cache,j,0,0)
               
                  IF output[0] EQ -1 THEN cache.conoff[*,j] = [-1,-1]                        ; If we get -1, then store -1,-1 as a flag
               ENDFOR
               
               FOR j=0,n_elements(cache.tablelist)-1,1 DO BEGIN
                  IF cache.conoff[0,j] NE -1 && cache.conoff[1,j] NE -1 THEN BEGIN           ; Check to see if current table returned a [-1,-1]
                     output = makecon(cache,j,0,0)                                           ; Get contour output from table
                     sz     = size(output)                                                   ; get the size of the output

                     if sz[0] EQ 1 then begin                                                ; If we have a 1D array, we just need to use a regular plot
                        IF flag EQ 0 THEN BEGIN
                           plot,output,XTITLE='Column',YTITLE='Row'
                           flag = 1
                        ENDIF ELSE BEGIN
                           oplot,output,color=(j*250/indcnt)
                        ENDELSE
                     ENDIF ELSE BEGIN                                                        ; If we get a 2d array, we need to do a touch more work
                                                                                             ; NOTE: Account for 2d and 1d mix?
                        xval   = xvals(cache)                                                ; Get the tick names

                        difx   = cache.conoff[0,j] - max([min(cache.conoff[0,*]),0])         ; Find the maximum offset between tabs in the X direction
                        dify   = cache.conoff[1,j] - max([min(cache.conoff[1,*]),0])         ; Find the maximum offset between tabs in the Y direction

                        output=makecon(cache,j,difx,dify)                                    ; Get the contour output, only now shift it according to difx and dify

                        IF flag EQ 0 THEN BEGIN
                           contour,output,xtickname=xval[0,*],ytickname=xval[1,*],XTITLE='Column',YTITLE='Row',NLEVELS=5
                           flag = 1
                        ENDIF ELSE BEGIN
                           contour,output,color=(j*250/indcnt),/OVERPLOT,NLEVELS=5
                        ENDELSE
                     ENDELSE
                  ENDIF
               ENDFOR
            ENDELSE
         ENDIF
         
         IF cache.plottype NE 1 THEN BEGIN                                          ; If draw widget is not displaying TVSCL, come up with a legend for plots
            X = [-2,0,2]                                                            ; Our symbols for the legend are just the line defined by [Xi,Yi]
            y = [0,0,0]
            usersym,x,y
   
            legname = strarr(indcnt)                                                ; Initialize the string array of legend entry text
            legsym  = intarr(indcnt)+8                                              ; Set the legend symbol array to 8, which uses the usersym output
            col     = intarr(indcnt)                                                ; Array of colors to use for each legend entry
            indname = get_tags(cache.indices)                                       ; Get the indice names for the active tabs
   
            for a=0,indcnt-1,1 do begin
               numstr     = strmid(indname[a],4)
               legname[a] = 'Tab '+numstr
               col[a]     = (a*250/indcnt)
            endfor
   
            col[0]=250

            legend,legname,psym=legsym,colors=col                                   ; Plot a legend
         ENDIF

         IF cache.disptype EQ "histogram" AND cache.plottype NE 1 THEN BEGIN        ; If we are using the histogram feature, we should also show the binsize and minimum
            INFOSTR = "Minimum is: "+strtrim(string(min(float(output))),2)
            xyouts,75,60,INFOSTR,/DEVICE
            
            INFOSTR = "Binsize is: "+strtrim(string(float(cache.binsize[0])),2)
            xyouts,75,50,INFOSTR,/DEVICE
         ENDIF
         
         
         XMANAGER, 'zoomed', LRGmain
      ENDIF
      
      IF ev.CLICKS EQ 1 AND ev.PRESS EQ 4 AND cache.plottype EQ 1 then begin         ; If a single right click, set a point for user defined selection from draw widget
         WIDGET_CONTROL, cache.coord1, GET_VALUE=coord1 
         WIDGET_CONTROL, cache.coord2, GET_VALUE=coord2
         WIDGET_CONTROL, cache.tablelist[itab], GET_VALUE=iTable
         
         sz   = dimensions(iTable)
         xdim = sz.sx
         ydim = sz.sy
         
         xfac = xdim/300.
         yfac = ydim/300.

         xpos = strtrim(string(int(ev.x*xfac)),1)
         ypos = strtrim(string(int(ev.y*yfac)),1)
  
         IF coord1 EQ "(x0,y0): " THEN BEGIN
            WIDGET_CONTROL, cache.coord1, SET_VALUE='(x0,y0): ('+xpos+','+ypos+')'
         ENDIF
         
         IF coord1 NE "(x0,y0): " AND coord2 EQ "(x1,y1): " THEN BEGIN
            WIDGET_CONTROL, cache.coord2, SET_VALUE='(x1,y1): ('+xpos+','+ypos+')'
         ENDIF
         
         IF coord1 NE "(x0,y0): " AND coord2 NE "(x1,y1): " THEN BEGIN
            WIDGET_CONTROL, cache.coord1, SET_VALUE='(x0,y0): '
            WIDGET_CONTROL, cache.coord2, SET_VALUE='(x1,y1): '
         ENDIF
      ENDIF
   ENDIF
end

pro histogram,pdsf,tabitr
   input  = [0]                                                                        ; Dummy array to first initialize the table with
   cache  = {disptype:"histogram",histmins:float(-999)}                                       ; Initialize the cache variable, which will store most of our info

   ;;; Create the Widget Bases
   main         = widget_base(mbar=BAR,/COLUMN)
   top          = widget_base(main,/ROW)
   tree_base    = widget_base(top,/COLUMN)
   preview_base = widget_base(top,/ROW)
   setting_base = widget_base(top,/COLUMN,XSIZE=300,YSIZE=300)
   coord_base   = widget_base(setting_base,/COLUMN)
   zoom_base    = widget_base(setting_base,/ROW)
   bin_base     = widget_base(setting_base,/ROW)
   prefbase     = widget_base(setting_base,/ROW,/EXCLUSIVE)

   ;;; Create Main Menu
   filemenu  = WIDGET_BUTTON(bar, VALUE='File', /MENU)
   file_save = WIDGET_BUTTON(filemenu, VALUE='Save Selection',EVENT_FUNC='save')
   file_exit = WIDGET_BUTTON(filemenu, VALUE='Exit',EVENT_FUNC="exit")
   helpmenu  = WIDGET_BUTTON(bar, VALUE='Help', /MENU)
   help_help = WIDGET_BUTTON(helpmenu, VALUE='Help',EVENT_FUNC="helpinfo")
 

   ;;; Read in tags for PDS structure
   ;;; NOTE: This is a stupid way to do things, but the best I could come up with at this time....
   tags    = get_tags(pdsf)                                 ; Call outside procedure, get_tags.pro, to find tags in a structure
   tagtree = widget_tree(tree_base,XSIZE=300,YSIZE=300)     ; Create Tree Widget that will house PDS Structure tags
   leafid  = strarr(1)                                      ; Initialize array to store leaves in the Tree Widget
   path    = ''                                             ; Initialize variable that will hold the path leading to a bottom element in a structure
   
   for i=0,n_elements(tags)-1,1 do begin
      iObj = tags[i]
      void = EXECUTE('iType = type(pdsf'+iObj+')')                   ; Find the type of the current tag
      
      if iType NE 8 then begin                                       ; Type 8 is a structure. We want the bottom elements. Skip Structures.

         ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;      
         ;;; To display COLUMN objects by their given name, we need to extract  ;;;
         ;;; information from the NAMES column. We find these names, store them ;;;
         ;;; then assign them to anything that matches *COLUMN*.                ;;;
         ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
         namesplt = strsplit(iObj,'NAMES',/extract,/regex)           ; Split the current element to test for possible NAMES column

         IF strmid(namesplt,strlen(namesplt)-1) EQ '.' THEN BEGIN    ; We expect any matches to leave a substring ending in '.'
            exstr   = 'names = pdsf'+iObj                            ; Save the names column to a regular variable 'names'
            void    = EXECUTE(exstr)
            path    = namesplt
            nameind = 0                                              ; Iterand for going through names in later code
         ENDIF

         colsplit = strsplit(iObj,'COLUMN',/extract,/regex)          ; If we can break up the element by COLUMN and match it to 'path', we need to assign a name

         ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
         ;;; Dynamically assign a leaf to the renamed PDS column. Note ;;; 
         ;;; that UVALUE stores the original tag name. This is needed  ;;;
         ;;; for later code so we can call on the contents properly.   ;;;
         ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
         IF colsplit[0] EQ path THEN BEGIN
            exstr  = 'tagleaf'+strtrim(string(i),1)+' = WIDGET_TREE(tagtree,VALUE="'+path+names[nameind]+'",UVALUE="'+iObj+'",event_func="newform")'
            void   = EXECUTE(exstr[0])
            nameind = nameind+1
         ENDIF ELSE BEGIN
            exstr  = 'tagleaf'+strtrim(string(i),1)+' = WIDGET_TREE(tagtree,VALUE="'+iObj+'",UVALUE="'+iObj+'",event_func="newform")'
            void   = EXECUTE(exstr)
         ENDELSE

         exstr  = 'leafid = [leafid,tagleaf'+strtrim(string(i),1)+']'
         void   = EXECUTE(exstr)
      endif
   endfor
   leafid  = leafid[1:n_elements(leafid)-1]        ; Strip away the initial blank string we used to initialize this variable
   
   
   ;;; Build the Preview Window
   preview = WIDGET_DRAW(preview_base, XSIZE=300, YSIZE=300, /BUTTON_EVENTS,RETAIN=2)


   ;;; Build the Settings Window
   zoomlbl      = widget_label(zoom_base,value="Zoom:    ")
   zoom         = widget_text(zoom_base,value=strtrim(string(1),1),/EDITABLE)
   binsizelbl   = widget_label(bin_base,value="Binsize: ") 
   binsize      = widget_text(bin_base,value=strtrim(string(1),1),/EDITABLE)
   tvscl_button = widget_button(setting_base, value="TVSCL", EVENT_FUNC="tvscl",ysize=40,xsize=50)
   coord1lbl    = widget_label(coord_base,value="(x0,y0): ",XS=200,/ALIGN_LEFT)
   coord2lbl    = widget_label(coord_base,value="(x1,y1): ",XS=200,/ALIGN_LEFT)
   highbutton   = widget_button(setting_base,value="Verify/Highlight Selection",EVENT_FUNC="highlight",ysize=40,xsize=50)
   histbutton   = widget_button(setting_base,value="Plot",ysize=40,xsize=50,EVENT_FUNC='plothist')
   pref_hist    = WIDGET_BUTTON(prefbase,VALUE="Histogram",EVENT_FUNC='disptype_hist')
   pref_dist    = WIDGET_BUTTON(prefbase,VALUE="Distribution",EVENT_FUNC='disptype_dist')
   tab          = WIDGET_TAB(main,YS=570)


   tbstr  = 'tablelist=['                          ; Initialize tbstr. Contains a list of user specified tabs and their names

   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   ;;; Concatenate the needed commands to create the user ;;;
   ;;; tabs and their tables. Then store their name into  ;;;
   ;;; into tbstr for later use.                          ;;;
   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   FOR tableIDX=0,tabitr-1,1 DO BEGIN
      exstr = 'tab'+strtrim(string(tableIDX+1),1)+'_base= WIDGET_BASE(tab,TITLE="TABLE '+strtrim(string(tableIDX+1),1)+'")'
      void  = EXECUTE(exstr)

      exstr = 'table'+strtrim(string(tableIDX+1),1)+'= widget_table(tab'+strtrim(string(tableIDX+1),1) $ 
              +'_base,value=input,/disjoint_selection,SCR_XSIZE=920,SCR_YSIZE=570,UVALUE=pdsf)'
      void  = EXECUTE(exstr)
      
      tbstr = tbstr+'table'+strtrim(string(tableIDX+1),1)+','
   ENDFOR

   tbstr = strmid(tbstr,0,strlen(tbstr)-1)+']'     ; Clean up the tbstr and concatenate the final character to form a list
   void  = EXECUTE(tbstr)

   conoff = intarr(2,tabitr)                       ; Initialize Contour offsets

   DEVICE, DECOMPOSED=0                            ; Ensure the draw widget is not destroyed when it goes to the background
   loadct,2                                        ; Load a color pallet so the user can easily distinguish different graphs
   WIDGET_CONTROL, main, /realize
   WIDGET_CONTROL, pref_hist, /SET_BUTTON          ; Solely cosmetic. Shows this button selected to start with.
   WIDGET_CONTROL, preview, GET_VALUE=drawID       ; We can only get the ID of the draw widget after it has been realized

   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   ;;; The most important variable in the program. Stores the WIDGET IDs and some important flags ;;;
   ;;; that the other functions need to operate. Be careful modifying this.                       ;;;
   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   cache  = create_struct(cache,"tagtree",tagtree,"leafid",leafid,"binsize",binsize,"tablelist",tablelist,"preview",drawid,"zoom",zoom, $
                          "coord1",coord1lbl,"coord2",coord2lbl,"plottype",0,"prefh",pref_hist,"prefd",pref_dist,"Tab",tab,"conoff",conoff)

   WIDGET_CONTROL, main, SET_UVALUE=cache    ; Assign cache to the UVALUE of main. This way other widgets and events can grab it
   XMANAGER, 'histogram', main               ; Boot off main to the XMANAGER. I have not tested this on systems where XMANAGER settings are not straightforward.
end