Viewing contents of file '../idllib/idl_5.2/lib/ascii_template.pro'
; $Id: ascii_template.pro,v 1.18.4.1 1999/01/16 16:37:11 scottm Exp $
;
; Copyright (c) 1996-1999, Research Systems, Inc. All rights reserved.
; Unauthorized reproduction prohibited.
;+
; NAME:
; ASCII_TEMPLATE
;
; PURPOSE:
; Generate a template that defines an ASCII file format.
;
; CATEGORY:
; Input/Output.
;
; CALLING SEQUENCE:
; template = ASCII_TEMPLATE( [file] )
;
; INPUTS:
; file - Name of file to base the template on.
; Default = use DIALOG_PICKFILE to get the file.
;
; INPUT KEYWORD PARAMETERS:
; browse_lines - Number of lines to read in at a time via the
; GUI's browse button. Default = 50.
;
; OUTPUT KEYWORD PARAMETERS:
; cancel - Boolean indicating if the user canceled
; out of the interface (1B = canceled).
;
; OUTPUTS:
; The function returns a template (structure) defining ASCII files
; of the input file's format. Such templates may be used as inputs
; to function READ_ASCII. (0 is returned if the user canceled.)
;
; COMMON BLOCKS:
; None.
;
; SIDE EFFECTS:
; None.
;
; RESTRICTIONS:
; See DESCRIPTION.
;
; DESCRIPTION:
; This routine presents a graphical user interface (GUI) that assists
; the user in defining a template.
;
; ASCII files handled by this routine consist of an optional header
; of a fixed number of lines, followed by columnar data. Files may
; also contain comments, which exist between a user-specified comment
; string and the corresponding end-of-line.
;
; One or more rows of data constitute a "record." Each data element
; within a record is considered to be in a different column, or "field."
; Adjacent fields may be "grouped" into multi-column fields.
; The data in one field must be of, or promotable to, a single
; type (e.g., FLOAT).
;
; EXAMPLES:
; ; Generating a template to be used later, maybe on a set of files.
; template = ASCII_TEMPLATE()
;
; ; Same as above, but immediately specifying which file to use.
; template = ASCII_TEMPLATE(file)
;
; ; Same as above, but returning flag if the user canceled.
; template = ASCII_TEMPLATE(file, CANCEL=cancel)
;
; ; Generating and using a template in place for reading data.
; data = READ_ASCII(file, TEMPLATE=ASCII_TEMPLATE(file))
;
; DEVELOPMENT NOTES:
; - see ???,!!!,xxx in the code
; - errors preserving state when switch pages with 'back/next'
; - make NaN default missing value as in reader ?
;
; MODIFICATION HISTORY:
; AL & RPM, 8/96 - Written.
;
;-
;
; ROUTINES:
; fun at_build_templ - build a template
; pro at_delete_template - delete a template
; fun at_get_lines - read lines from file
; fun at_build_template - build a template (calls at_build_templ)
; fun at_num_fields - return number of fields/line for record lines
; fun at_default_delimit - guess delimiter based on first data line
; fun at_default_groups - (not currently used : grouping by default)
; fun at_which_field - return the line and column of a given field
; fun at_str_to_val - convert a string to a long or float
; fun at_list_to_str - convert a vector to comma-separated string
; fun at_str_to_list - convert a comma-separated string to vector
; pro at_remove_tabs - replace tabs in string array with 4 spaces
; pro at_display_text - display data in the main table widget
; pro at_resize_table - reset number of rows/cols in table widget
; pro at_sample_record - display sample record in 3rd step
; pro at_set_list - display field-specification in 3rd step
; pro at_update - update the 3rd step page
; pro at_3_event - handle events for 3rd page of GUI
; pro at_2_event - handle events for 2nd page of GUI
; pro at_1_event - handle events for 1st page of GUI
; pro at_set_state - set page widget for initial, Next, and Back
; pro at_widget_cleanup - destroy widgets and free heap
; pro at_widget_event - handle buttons at the bottom of the dialog
; fun at_widget - create the widgets
; fun at_check_file - check validity of input file
; fun ascii_template - the main routine
; -----------------------------------------------------------------------------
;
; Purpose: Build an ASCII file template, using defaults as necessary.
;
function at_build_templ, $
num_fields=num_fields, $
field_types=field_types, $
field_names=field_names, $
field_locations=field_locations, $
record_start_loc=record_start_loc, $
delimiter=delimiter, $
groups=groups, $
missing_value=missing_value, $
comment_symbol=comment_symbol
if (n_elements(num_fields) eq 0) then num_fields = 1
tot_num_fields = total(num_fields)
if (n_elements(field_types) eq 0) then field_types = 4L
if (n_elements(field_locations) eq 0) then $
field_locations = lonarr(tot_num_fields)
if (n_elements(field_names) eq 0) then begin
digits_str = string(strlen(strtrim(string(fix(tot_num_fields)),2)))
fstr = '(i' + strtrim(digits_str,2) + '.' + strtrim(digits_str,2) + ')'
field_names = 'field' + string(indgen(tot_num_fields)+1,format=fstr)
endif
if (n_elements(record_start_loc) eq 0) then record_start_loc = 0L
if (n_elements(delimiter) eq 0) then delimiter = 0B
if (n_elements(missing_value) eq 0) then missing_value = 0.0
if (n_elements(groups) eq 0) then groups = indgen(tot_num_fields)
if (n_elements(comment_symbol) eq 0) then comment_symbol = ''
; define the ASCII template structure
;
fields = replicate({ascii_field_struct, name:'', type:0, loc:0L}, $
tot_num_fields)
if (tot_num_fields eq 1) then begin
fields.name = field_names[0]
fields.type = field_types[0]
fields.loc = field_locations[0]
endif else begin
fields.name = field_names
fields.type = field_types
fields.loc = field_locations
endelse
template = { $
record_start_loc: record_start_loc, $
delimit: delimiter[0], $
missing_value: float(missing_value), $ ; Why FLOAT() ???
comment_symbol: comment_symbol, $
p_num_fields: ptr_new(num_fields), $
p_fields: ptr_new(fields), $
p_groups: ptr_new(groups) $
}
return, template
end ; at_build_templ
; -----------------------------------------------------------------------------
;
; Purpose: Delete an ASCII file template.
;
pro at_delete_template, template
if (size(template,/type) ne 8) then return
if (ptr_valid(template.p_num_fields)) then ptr_free, template.p_num_fields
if (ptr_valid(template.p_fields)) then ptr_free, template.p_fields
if (ptr_valid(template.p_groups)) then ptr_free, template.p_groups
end ; at_delete_template
; -----------------------------------------------------------------------------
;
; Purpose: Return a requested number of lines from the ASCII file.
; If skip is set, then skip the first skip number of lines before
; starting the read.
; Return the last_pos to resume reading from a given point and
; notify when the end of file has been reached.
;
function at_get_lines, name, num_lines, last_pos=last_pos, $
end_reached=end_reached, skip=skip
catch, error_status
if (error_status ne 0) then begin
end_reached = 1
if (n_elements(unit) gt 0) then free_lun, unit
return, ''
endif
if (n_elements(last_pos) eq 0) then last_pos = 0
if (n_elements(skip) eq 0) then skip = 0
lines = strarr(num_lines)
line = ''
count = 0L
openr, unit, name, /get_lun
point_lun, unit, last_pos
for i=0, skip-1 do readf, unit, line
while (not eof(unit) and count lt num_lines) do begin
readf, unit, line
lines[count] = line
count = count + 1
endwhile
end_reached = (count lt num_lines)
point_lun, -unit, last_pos
free_lun, unit
if (count eq 0) then return, '' $
else if (count lt num_lines) then return, lines[0:count-1] $
else return, lines
end ; at_get_lines
; -----------------------------------------------------------------------------
;
; Purpose: Build a default template structure.
;
function at_build_template, data, missing_value
; Use the first record from the file (ignoring comment strings
; and blank lines).
;
lines = (*data.p_rlines)[0:n_elements(*data.p_num_fields)-1]
; lut is used to determine if a given field is integer,
; floating point, or a string.
;
lut = bytarr(256) + 1b
lut[0:32] = 0b
lut[48:57] = 0b
lut[byte('e')] = 0
lut[byte('E')] = 0
lut[byte('+')] = 0
lut[byte('-')] = 0
lut[byte('.')] = 0
; scan through a sample record and determine a default column
; position and IDL type for each field.
;
tot_num_fields = long(total(*data.p_num_fields))
field_locations = lonarr(tot_num_fields)
field_types = intarr(tot_num_fields)
fpos = 0L
for i=0, n_elements(*data.p_num_fields)-1 do begin
bline = [byte(lines[i]), 32b]
if (data.delimit eq 32b) then begin ; delimiter is 'space'
nptr = where(bline ne 32b and bline ne 9b, ncount)
fptr = nptr[0]
for j=1, ncount-1 do $
if (nptr[j] gt nptr[j-1]+1) then fptr = [fptr, nptr[j]]
fptr = [fptr, n_elements(bline)]
add = [0,1]
endif else begin
nptr = where(bline eq data.delimit, ncount)
if (ncount eq 0) then fptr = [-1, n_elements(bline)] $
else fptr = [-1, nptr, n_elements(bline)]
add = [1,1]
endelse
for j=0, (n_elements(fptr)-2)<((*data.p_num_fields)[i]-1) do begin
field_locations[fpos] = fptr[j] + add[0]
bsub = bline[fptr[j]+add[0]:(fptr[j+1]-add[1])>(fptr[j]+add[0])]
is_string = (total(lut[bsub]) gt 0)
if (is_string eq 0) then begin
if (total(bsub eq 46b) gt 0 or total(bsub eq 101b) gt 0 or $
total(bsub eq 69b) gt 0) then field_types[fpos] = 4 $
else $
field_types[fpos] = 3
endif else $
field_types[fpos] = 7
fpos = fpos + 1
endfor
endfor
s_delimit = ([0b, data.delimit])[data.mode]
template = at_build_templ(num_fields=*data.p_num_fields, $
field_types=field_types, field_locations=field_locations, $
record_start_loc=data.data_start, delimiter=s_delimit, $
missing_value=missing_value, comment_symbol=data.comment)
return, template
end ; at_build_template
; -----------------------------------------------------------------------------
;
; Purpose: Return an array containing the number of fields/line
; for each line of a record (based upon the delimiters found).
;
function at_num_fields, lines, delimit, comment
for i=0, n_elements(lines)-1 do begin
bline = byte(lines[i])
if (delimit eq 32b) then begin ; delimiter is 'space'
nptr = where(bline ne 32b and bline ne 9b, count)
fptr = nptr[0]
for j=1, count-1 do $
if (nptr[j] gt nptr[j-1]+1) then fptr = [fptr, nptr[j]]
endif else begin ; delimiter is not 'space'
nptr = where(bline eq delimit, count)
fptr = bytarr(count+1)
endelse
if (n_elements(num_fields) eq 0) then $
num_fields = n_elements(fptr) $
else begin
if (n_elements(fptr) eq num_fields[0]) then return, num_fields
num_fields = [num_fields, n_elements(fptr)]
endelse
endfor
if (n_elements(num_fields) gt 0) then return, num_fields $
else return, 1
end ; at_num_fields
; -----------------------------------------------------------------------------
;
; Purpose: Given the first line of data make a guess as to the delimiter
; in use.
;
function at_default_delimit, line, comment
if (comment ne '') then begin
pos = strpos(line, comment, 0)
if (pos[0] ge 0) then line = strmid(line, 0, pos[0]-1)
endif
pos = strpos(line, ',')
if (pos[0] ge 0) then return, 44B
pos = strpos(line, ';')
if (pos[0] ge 0) then return, 59B
; don't assume that the delimiter can be a colon...
; pos = strpos(line, ':')
; if (pos[0] ge 0) then return, 58B
; Default return 'space' as the delimiter.
;
return, 32B
end ; at_default_delimit
; -----------------------------------------------------------------------------
;
; THIS FUNCTION IS NOT CURRENTLY USED !!!!!
;
; Purpose: Return a best guess of the groupings of data base upon initial
; data type.
;
;function at_default_groups, data
;
; types = (*data.p_fields).type
; groups = intarr(n_elements(types))
; cur_group = 0
;
; for i=1, n_elements(types)-1 do begin
; if (types[i] eq types[i-1]) then groups[i] = groups[i-1] $
; else begin
; cur_group = cur_group + 1
; groups[i] = cur_group
; endelse
; endfor
;
; return, groups
;
;end ; at_default_groups
; -----------------------------------------------------------------------------
;
; Purpose: Given a position between 0 and tot_num_fields-1 determine
; the line and location on the line of the given field.
;
function at_which_field, $
num_fields, $ ; IN:
pos, $ ; IN: the sequential field to check
col ; OUT: the corresponding column that the field is in
count = 0
for i=0, n_elements(num_fields)-1 do begin
if (pos lt count+num_fields[i]) then begin
col = pos - count
; Return the sequential line that the field is in.
;
return, i
endif
count = count + num_fields[i]
endfor
end ; at_which_field
; -----------------------------------------------------------------------------
;
; Purpose: Convert a scalar string into a long or floating point value;
; return 0 for any problems.
;
function at_str_to_val, str, floating=floating
catch, error_status
if (error_status ne 0) then return, 0
if (keyword_set(floating)) then temp = 0. $
else temp = 0L
reads, str[0], temp
return, temp
end ; at_str_to_val
; -----------------------------------------------------------------------------
;
; Purpose: Convert an array of numbers into a string of comma separated
; values.
;
function at_list_to_str, vals
str = strtrim(string(vals[0]),2)
for i=1, n_elements(vals)-1 do $
str = str + ',' + strtrim(string(vals[i]),2)
return, str
end ; at_list_to_str
; -----------------------------------------------------------------------------
;
; Purpose: Given a string of comma separated values, convert into an array
; of values.
;
function at_str_to_list, str
len = strlen(str[0])
ptr = where(byte(str[0]) eq 44b, count)
sub = (strmid(str[0],len-1,1) eq ',')
count = count - sub
vals = lonarr(count+1)
vals[0] = at_str_to_val(str[0])
for i=0, count-1 do $
vals[i+1] = at_str_to_val(strmid(str[0],ptr[i]+1,len))
return, vals
end ; at_str_to_list
; -----------------------------------------------------------------------------
;
; Purpose: Scan through the string array and replace any instance of
; tab (9b) with four white spaces.
;
pro at_remove_tabs, lines
s_tab = string(9b)
for i=0, n_elements(lines)-1 do begin
loc = 0l
len = strlen(lines[i])
repeat begin
pos = strpos(lines[i], s_tab, loc)
if (pos ge 0) then begin
lines[i] = strmid(lines[i], 0, pos) + ' ' + $
strmid(lines[i], pos+1, len)
loc = pos + 1
endif
endrep until (pos eq -1)
endfor
end ; at_remove_tabs
; -----------------------------------------------------------------------------
;
; Purpose: Display the appropriate text into the main table widget.
;
pro at_display_text, data, first=first
if (keyword_set(first)) then begin
top_pos = widget_info(data.tw[6], /table_view)
num_lines = n_elements(*data.p_lines)
rstr = strtrim(string(indgen(num_lines)+1),2)
; remove any tabs
if (total(byte(*data.p_lines) eq 9b) gt 0) then begin
lines = *data.p_lines
at_remove_tabs, lines
lines = reform(lines, 1, num_lines, /overwrite)
endif else $
lines = reform(*data.p_lines, 1, num_lines)
widget_control, data.tw[6], set_table_view=top_pos, set_value=lines, $
row_labels=rstr, set_table_select=[0,data.data_start<(data.twm[1]-1), $
0,data.data_start<(data.twm[1]-1)]
endif else begin
num_lines = n_elements(*data.p_rlines)
rstr = strtrim(string(indgen(num_lines)+1),2)
; remove any tabs
if (total(byte(*data.p_rlines) eq 9b) gt 0) then begin
lines = *data.p_rlines
at_remove_tabs, lines
lines = reform(lines, 1, num_lines, /overwrite)
endif else $
lines = reform(*data.p_rlines, 1, num_lines)
widget_control, data.tw[6], set_table_view=[0,0], set_value=lines, $
row_labels=rstr, set_table_select=[-1,-1,-1,-1]
endelse
end ; at_display_text
; -----------------------------------------------------------------------------
;
; Purpose: Resize a given table widget to a new number of row / col cells.
;
pro at_resize_table, tw, prev_size, new_size, text_table=text_table, last=last
; There are two different table widgets used in this widget. If the text_table
; keyword is set, then this refers to the table widget on the bottom which
; displays the text in STEP 1, the data in STEP 2 and a sample record in
; STEP 3. If not set, then this refers to the table wdiget in STEP 3 in the
; upper left corner which displays the field information.
if (prev_size[0] eq new_size[0] and prev_size[1] eq new_size[1]) then return
col_diff = prev_size[0] - new_size[0]
row_diff = prev_size[1] - new_size[1]
if (row_diff gt 0) then begin
widget_control, tw, delete_rows=row_diff, $
use_table_select=[0,new_size[1],prev_size[0]-1,prev_size[1]-1]
endif else if (row_diff lt 0) then begin
widget_control, tw, insert_rows=-row_diff
endif
if (col_diff gt 0) then begin
widget_control, tw, delete_columns=col_diff, $
use_table_select=[new_size[0],0,prev_size[0]-1,new_size[1]-1]
endif else if (col_diff lt 0) then begin
widget_control, tw, insert_columns=-col_diff
endif
if (keyword_set(text_table)) then begin
; the text table has a different look in STEP 3 than in STEP 1 & 2...
;
if (keyword_set(last)) then $
; widget_control, tw, column_widths=.75, units=1 $
widget_control, tw, column_widths=90 $
else begin
widget_control, tw, column_widths=450, column_labels=['Text']
widget_control, tw, column_widths=40, use_table_select=[-1,0,-1,0]
endelse
endif
end ; at_resize_table
; -----------------------------------------------------------------------------
;
; Purpose: Organize and display a sample record of n lines to demonstrate
; how the current defined template interprets the ASCII file.
;
pro at_sample_record, data
num_fields = *data.p_num_fields
lines = (*data.p_rlines)[0:n_elements(num_fields)-1]
new_twm = [max(num_fields), n_elements(num_fields)]
at_resize_table, data.tw[6], data.twm, new_twm, /text_table, /last
data.twm = new_twm
str = strarr(new_twm[0], new_twm[1])
fpos = 0
; Loop for each field.
;
for i=0, n_elements(num_fields)-1 do begin
for j=0, num_fields[i]-1 do begin
if (j eq num_fields[i]-1) then $ ; last field
len = strlen(lines[i]) - (*data.p_fields)[fpos].loc $
else $ ; not last field
len = (*data.p_fields)[fpos+1].loc - (*data.p_fields)[fpos].loc - 1
str[j,i] = strtrim(strmid(lines[i], (*data.p_fields)[fpos].loc, len),2)
fpos = fpos + 1
endfor
endfor
widget_control, data.tw[6], set_value=str
end ; at_sample_record
; -----------------------------------------------------------------------------
;
; Purpose: Organize and display the field specifications into a table widget.
;
pro at_set_list, data, just_highlight=just_highlight
groups = *data.p_groups
; If NOT just highlighting...
;
if (keyword_set(just_highlight) eq 0) then begin
dstr = ['Skip', 'Byte', 'Integer', 'Long', 'Floating', 'Double', $
'Complex', 'String']
; Set new table widget size.
;
new_tws = [ ([3,2])[data.mode], long(total(*data.p_num_fields)) ]
at_resize_table, data.tw[8], data.tws, new_tws
data.tws = new_tws
; String array, set to [3,1] in at_widget.
; [0,0] =
; [1,0] =
; [2,0] =
;
str = strarr(data.tws[0], data.tws[1])
gptr = 0
; Loop for each group.
;
for i=0, n_elements(groups)-1 do begin
if (i eq 0) then new_group = 1 $
else new_group = (groups[i] ne groups[i-1])
ptr = where(groups eq groups[i], count)
if (new_group) then begin
gptr = i
if (count eq 1) then str[0,i] = (*data.p_fields)[i].name $
else str[0,i] = '{1} ' + (*data.p_fields)[i].name
str[1,i] = dstr[(*data.p_fields)[i].type]
if (data.mode eq 0) then $
str[2,i] = strtrim(string((*data.p_fields)[i].loc),2)
endif else begin
str[0,i] = '{' + strtrim(string(i-gptr+1),2) + '}'
if (data.mode eq 0) then $
str[2,i] = strtrim(string((*data.p_fields)[i].loc),2)
endelse
endfor
; Set column labels in main table widget.
;
if (n_elements(*data.p_num_fields) eq 1) then $
c_str = reform(str[0,*]) $
else $
c_str = strarr(data.twm[0]) + ' '
widget_control, data.tw[6], column_label=c_str
; Set value and row labels in upper-left table widget.
;
rstr = strtrim(string(indgen(data.tws[1])+1),2)
widget_control, data.tw[8], set_value=str, row_labels=rstr, alignment=0
endif
; Set table selection in upper-left table widget.
;
widget_control, data.tw[8], set_table_select= $
[0, data.lptr, data.tws[0]-1, data.lptr]
; Set selection in main table widget corresponding to the field selected.
;
lpos = at_which_field(*data.p_num_fields, data.lptr, col)
widget_control, data.tw[6], set_table_select=[col,lpos,col,lpos]
; Set sensitivity of Type droplist and Ungroup button.
; (For Type, is sensitive if on the first entry of the group.)
; (For Ungroup, is sensitive if on the first entry of the group,
; and more than one entry in the group.)
;
ptr = where(groups eq groups[data.lptr], count)
widget_control, data.dl, sensitive=(ptr[0] eq data.lptr)
widget_control, data.buts[14], sensitive=(ptr[0] eq data.lptr and count gt 1)
end ; at_set_list
; -----------------------------------------------------------------------------
;
; Purpose: Update the 3rd step page.
;
pro at_update, data, new_lptr, change=change, new=new
if (n_elements(change) eq 0) then change = 0
if (keyword_set(new) eq 0) then begin
; Get Name.
; Get Column number (used for Fixed Width data organization).
;
widget_control, data.tw[4], get_value=name
widget_control, data.tw[5], get_value=col
; Check if name or column was changed.
;
change = change or (name[0] ne (*data.p_fields)[data.lptr].name or $
long(col[0]) ne (*data.p_fields)[data.lptr].loc)
(*data.p_fields)[data.lptr].name = name[0]
(*data.p_fields)[data.lptr].loc = long(col[0])
endif
data.lptr = new_lptr
; Update field table widget (just highlight if not changing values).
;
at_set_list, data, just_highlight=(change eq 0)
; Fill in Name in text widget.
;
widget_control, data.tw[4], set_value=(*data.p_fields)[new_lptr].name
; Set the Type droplist selection.
;
widget_control, data.dl, set_droplist_select= $
(*data.p_fields)[new_lptr].type
; Set Column number (used for Fixed Width data organization).
;
widget_control, data.tw[5], set_value= $
strtrim(string((*data.p_fields)[new_lptr].loc),2)
end ; at_update
; -----------------------------------------------------------------------------
;
; Purpose: Handle events for third page of GUI.
;
pro at_3_event, ev
widget_control, ev.id, get_uvalue=uvalue
widget_control, ev.top, get_uvalue=data, /no_copy
if (uvalue eq 'group') then begin
; (Return selection is in form [left, top, right, bottom].)
;
sel = widget_info(data.tw[8], /table_select)
if (sel[1] ne sel[3]) then begin
types = (*data.p_fields)[sel[1]:sel[3]].type
is_string = (total(types eq 7) gt 0)
if (is_string) then $
(*data.p_fields)[sel[1]:sel[3]].type = (7 * (types ne 0)) $
else $
(*data.p_fields)[sel[1]:sel[3]].type = (max(types) * (types ne 0))
(*data.p_groups)[sel[1]:sel[3]] = (*data.p_groups)[sel[1]]
at_update, data, data.lptr, /change, /new
endif
endif
if (uvalue eq 'ungroup') then begin
ptr = where(*data.p_groups eq (*data.p_groups)[data.lptr])
(*data.p_groups)[ptr] = (indgen(n_elements(*data.p_fields)))[ptr]
(*data.p_fields)[ptr].type = (*data.p_types)[ptr]
at_set_list, data
endif
if (uvalue eq 'ungroup all') then begin
*data.p_groups = indgen(n_elements(*data.p_fields))
(*data.p_fields).type = *data.p_types
at_set_list, data
endif
if (uvalue eq 'ltable') then begin
; if selection event...
if (ev.type eq 4) then $
if (ev.sel_top eq ev.sel_bottom and ev.sel_top ne -1) then $
at_update, data, ev.sel_top
endif
if (uvalue eq 'table') then begin
; if selection event...
if (ev.type eq 4) then $
if (ev.sel_top eq ev.sel_bottom and ev.sel_top ne -1) then begin
tot_num_fields = long(total(*data.p_num_fields))
if (ev.sel_top eq 0) then add = 0 $
else add = long(total((*data.p_num_fields)[0:ev.sel_top-1]))
new_ptr = (ev.sel_left + add) < (tot_num_fields-1)
at_update, data, new_ptr, /change
endif
endif
if (uvalue eq 'list') then begin
if (ev.index lt n_elements(*data.p_fields)) then $
at_update, data, ev.index
endif
if (uvalue eq 'name') then begin
if (ev.type eq 0) then begin
bad = (ev.ch lt 48 or (ev.ch gt 57 and ev.ch lt 65) or $
(ev.ch gt 90 and ev.ch lt 97 and ev.ch ne 95) or ev.ch gt 122l)
if (bad) then begin
widget_control, ev.id, set_value=(*data.p_fields)[data.lptr].name
widget_control, ev.id, set_text_select= $
strlen((*data.p_fields)[data.lptr].name)
endif
endif
if (keyword_set(bad) eq 0) then begin
widget_control, ev.id, get_value=name
if (name[0] ne (*data.p_fields)[data.lptr].name and $
name[0] ne '') then begin
(*data.p_fields)[data.lptr].name = name[0]
at_set_list, data
endif
endif
endif
if (uvalue eq 'type') then begin
ptr = where(*data.p_groups eq (*data.p_groups)[data.lptr], count)
if (count eq 1) then begin
(*data.p_fields)[data.lptr].type = ev.index
(*data.p_types)[data.lptr] = ev.index
endif else $
(*data.p_fields)[ptr].type = (ev.index * ((*data.p_types)[ptr] ne 0))
at_set_list, data
endif
if (uvalue eq 'location') then begin
widget_control, ev.id, get_value=str
(*data.p_fields)[data.lptr].loc = at_str_to_val(str)
at_set_list, data
; Organize and display a sample record of n lines to demonstrate
; how the current defined template interprets the ASCII file.
;
at_sample_record, data
endif
if (strmid(uvalue,0,4) eq 'miss') then begin
data.miss_type = fix(strmid(uvalue,4,1))
widget_control, data.mb[4], sensitive=data.miss_type
if (data.miss_type eq 0) then $
(*data.p_template).missing_value = !values.f_nan $
else begin
widget_control, data.tw[0], get_value=str
(*data.p_template).missing_value = at_str_to_val(str, /floating)
endelse
endif
if (uvalue eq 'value') then begin
widget_control, ev.id, get_value=str
(*data.p_template).missing_value = at_str_to_val(str, /floating)
endif
widget_control, ev.top, set_uvalue=data, /no_copy
end ; at_3_event
; -----------------------------------------------------------------------------
;
; Purpose: Handle events for second page of GUI.
;
pro at_2_event, ev
widget_control, ev.id, get_uvalue=uvalue
widget_control, ev.top, get_uvalue=data, /no_copy
if (uvalue eq 'user' or strmid(uvalue,0,7) eq 'delimit') then begin
if (uvalue eq 'user') then type = 5 $
else type = fix(strmid(uvalue,7,1))
data.delimit = ([9,59,32,44,58,32])[type]
widget_control, data.mb[5], sensitive=(type eq 5)
if (type eq 5) then begin
widget_control, data.tw[1], get_value=str
data.delimit = (byte(strmid(str[0],0,1)))[0]
endif
; Get the number of fields/line for each line in a record.
;
num_fields = at_num_fields(*data.p_rlines, data.delimit, data.comment)
widget_control, data.tw[2], set_value=at_list_to_str(num_fields)
*data.p_num_fields = temporary(num_fields)
data.change = 1
endif
if (uvalue eq 'fields') then begin
; Get the number of fields/line for each line in a record.
;
widget_control, ev.id, get_value=str
if (strtrim(strcompress(str[0]),2) eq '') then str = '1'
*data.p_num_fields = at_str_to_list(str)
data.change = 1
endif
widget_control, ev.top, set_uvalue=data, /no_copy
end ; at_2_event
; -----------------------------------------------------------------------------
;
; Purpose: Handle events for first page of GUI.
;
pro at_1_event, ev
widget_control, ev.id, get_uvalue=uvalue
widget_control, ev.top, get_uvalue=data, /no_copy
if (uvalue eq 'text') then begin
widget_control, ev.id, get_value=str
ds = (at_str_to_val(str)-1)
if (ds ne data.data_start) then begin
data.data_start = ds
widget_control, data.tw[6], set_table_select= $
[0,data.data_start<(data.twm[1]-1),0,data.data_start<(data.twm[1]-1)]
data.change = 1
endif
endif
if (uvalue eq 'comment') then begin
widget_control, ev.id, get_value=str
data.comment = str[0]
data.change = 1
endif
if (strmid(uvalue,0,4) eq 'mode') then begin
data.mode = fix(strmid(uvalue,4,1))
data.change = 1
endif
; Handle table events.
;
if (uvalue eq 'table') then begin
; if selection event...
if (ev.type eq 4) then $
if (ev.sel_left eq 0) then begin
widget_control, data.tw[7], set_value= $
strtrim(string(ev.sel_top+1),2)
data.change = 1
endif
; Handle "Next n Lines" button event.
;
endif else if (uvalue eq 'next set') then begin
widget_control, /hourglass
last_pos = data.last_pos
new_lines = at_get_lines(data.name, data.browseLines, $
last_pos=last_pos, end_reached=end_reached)
data.last_pos = last_pos
data.end_reached = end_reached
widget_control, data.mb[7], sensitive=(end_reached eq 0)
if (n_elements(new_lines) gt 1 or new_lines[0] ne '') then begin
*data.p_lines = [*data.p_lines, new_lines]
widget_control, data.tw[6], insert_rows=n_elements(new_lines)
at_display_text, data, /first
endif
endif
widget_control, ev.top, set_uvalue=data, /no_copy
end ; at_1_event
; -----------------------------------------------------------------------------
;
; Purpose: Control the setting of the three progessive states of the
; template definition (sets all of the widgets and pointers
; involved and allows movement both forward and backward).
;
pro at_set_state, $
data, $ ; IN:
forward=forward, $ ; IN: (opt)
back=back ; IN: (opt) [currently not used]
case (data.step) of
; ----------------------------------------
0: begin ; STEP 1
; ----------------------------------------
title = 'STEP 1 of 3: Define Data Type / Range'
widget_control, data.base, tlb_set_title=title
widget_control, data.buts[9], sensitive=0
widget_control, data.buts[0], set_button=(data.mode eq 0)
widget_control, data.buts[1], set_button=data.mode
widget_control, data.buts[13], sensitive=0
widget_control, data.buts[2], set_button=(data.miss_type eq 0)
widget_control, data.buts[3], set_button=data.miss_type
widget_control, data.mb[4], sensitive=data.miss_type
widget_control, data.slab, set_value='Selected Text File'
widget_control, data.mb[7], sensitive=(data.end_reached eq 0), map=1
widget_control, data.tw[7], set_value= $
strtrim(string(data.data_start+1),2)
widget_control, data.tw[6], event_pro='at_1_event'
at_display_text, data, /first
data.change = 0
end
; ----------------------------------------
1: begin ; STEP 2
; ----------------------------------------
; Retreive the data_start value.
;
widget_control, data.tw[7], get_value=str
data.data_start =(at_str_to_val(str)-1) > 0
; Scan through lines and remove comments and blank lines for
; steps two and three (after the start of the data).
;
if (keyword_set(forward)) then begin
lines = *data.p_lines
rlines = strarr(n_elements(lines))
count = 0
for i=data.data_start, n_elements(lines)-1 do begin
line = lines[i]
if (data.comment ne '') then begin
pos = strpos(line, data.comment)
if (pos[0] ne -1) then line = strmid(line, 0, pos[0]-1)
endif
if (strtrim(line,2) ne '') then begin
rlines[count] = line
count = count + 1
endif
endfor
if (count gt 0) then *data.p_rlines = rlines[0:count-1] $
else *data.p_rlines = ''
endif
widget_control, data.mb[3], map=data.mode
widget_control, data.buts[9], sensitive=1
widget_control, data.buts[10], sensitive=1
widget_control, data.buts[13], sensitive=0
widget_control, data.mb[7], map=0
widget_control, data.slab, set_value='Selected Text Records'
if (data.mode eq 1) then begin
if (keyword_set(forward)) then $
data.delimit = at_default_delimit((*data.p_rlines)[0], data.comment)
title = 'STEP 2 of 3: Define Delimiter / Fields'
widget_control, data.base, tlb_set_title=title
user_defined = (total([59b,58b,32b,44b] eq data.delimit) eq 0)
if (user_defined) then tstr = string(data.delimit) $
else tstr = ''
widget_control, data.tw[1], set_value=tstr
widget_control, data.mb[5], sensitive=user_defined
widget_control, data.buts[5], set_button=(data.delimit eq 59b)
widget_control, data.buts[6], set_button=(data.delimit eq 32b)
widget_control, data.buts[7], set_button=(data.delimit eq 44b)
widget_control, data.buts[11],set_button=(data.delimit eq 9b)
widget_control, data.buts[16], set_button=(data.delimit eq 58b)
widget_control, data.buts[8], set_button=user_defined
endif else begin
title = 'STEP 2 of 3: Define Fields'
widget_control, data.base, tlb_set_title=title
endelse
if ((keyword_set(forward) and data.change) or $
n_elements(*data.p_num_fields) eq 0) then $
num_fields = at_num_fields(*data.p_rlines, data.delimit,data.comment) $
else begin
num_fields = *data.p_num_fields
new_twm = [2, n_elements(*data.p_lines)]
at_resize_table, data.tw[6], data.twm, new_twm, /text_table
data.twm = new_twm
endelse
widget_control, data.tw[2], set_value=at_list_to_str(num_fields)
*data.p_num_fields = temporary(num_fields)
widget_control, data.tw[6], event_pro='at_2_event'
at_display_text, data
data.change = 0
end
; ----------------------------------------
2: begin ; STEP 3
; ----------------------------------------
title = 'STEP 3 of 3: Field Specification'
widget_control, data.base, tlb_set_title=title
widget_control, data.buts[10], sensitive=0
widget_control, data.mb[6], map=(data.mode eq 0)
widget_control, data.buts[13], sensitive=1
widget_control, data.slab, set_value='Sample Record'
if (data.miss_type eq 0) then $
miss_value = !values.f_nan $
else begin
widget_control, data.tw[0], get_value=str
miss_value = at_str_to_val(str)
endelse
if (n_elements(*data.p_template) eq 0 or data.change) then begin
; Build new template.
; Delete previous template.
; Save new template
;
template = at_build_template(data, miss_value)
at_delete_template, *data.p_template
*data.p_template = temporary(template)
; Assign a copy of pointers from the template to the data structure
; for simpler access.
;
data.p_fields = (*data.p_template).p_fields
data.p_groups = (*data.p_template).p_groups
*data.p_types = (*data.p_fields).type
endif
; Organize and display a sample record of n lines to demonstrate
; how the current defined template interprets the ASCII file.
;
at_sample_record, data
widget_control, data.tw[6], event_pro='at_3_event'
data.lptr = 0
at_update, data, data.lptr, /change, /new
end
endcase
end ; at_set_state
; -----------------------------------------------------------------------------
;
; Purpose:
;
pro at_widget_cleanup, base
widget_control, base, get_uvalue=data, /no_copy
widget_control, base, /destroy
if (n_elements(data) eq 0) then return
ptr_free, data.p_lines
ptr_free, data.p_rlines
ptr_free, data.p_num_fields
ptr_free, data.p_template
ptr_free, data.p_types
end ; at_widget_cleanup
; -----------------------------------------------------------------------------
;
; Purpose: Handle the buttons at the bottom of the dialog.
;
pro at_widget_event, ev
if (tag_names(ev,/struct) eq 'WIDGET_KILL_REQUEST') then begin
at_widget_cleanup, ev.top
return
endif
widget_control, ev.id, get_uvalue=uvalue
widget_control, ev.top, get_uvalue=data, /no_copy
if (uvalue eq 'next') then begin
widget_control, data.mb[data.step], map=0
data.step = data.step + 1
widget_control, data.mb[data.step], map=1
at_set_state, data, /forward
endif
if (uvalue eq 'back') then begin
widget_control, data.mb[data.step], map=0
data.step = data.step - 1
widget_control, data.mb[data.step], map=1
at_set_state, data, /back
endif
if (uvalue eq 'finish') then begin
*data.p_result = {accept:1, template:*data.p_template}
widget_control, ev.top, set_uvalue=data, /no_copy
at_widget_cleanup, ev.top
return
endif
if (uvalue eq 'cancel') then begin
at_delete_template, *data.p_template
widget_control, ev.top, set_uvalue=data, /no_copy
at_widget_cleanup, ev.top
return
endif
widget_control, ev.top, set_uvalue=data, /no_copy
end ; at_widget_event
; -----------------------------------------------------------------------------
;
; Purpose: Create the widgets.
;
function at_widget, $
name, $ ; IN:
GROUP=group, $ ; IN: (opt)
CANCEL=cancel, $ ; OUT:
BROWSE_LINES=browseLines ; IN:
; Set number of lines for browse button.
;
if (N_ELEMENTS(browseLines) ne 0) then browseLinesUse = browseLines $
else browseLinesUse = 50
; Check out the size of the screen and adjust the widget acordingly.
;
device, get_screen_size=screen_size
table_scr_ysize = ([110,210])[screen_size[1] gt 600]
xoff = ((screen_size[0] - 600) / 2) > 0
yoff = ((screen_size[1] - 600) / 2) > 0
dt_str = ['Skip Field', 'Byte', 'Integer', 'Long Integer', $
'Floating Point', 'Double Precision', 'Complex', 'String']
lines = at_get_lines(name, browseLinesUse, last_pos=last_pos, $
end_reached=end_reached)
buts = lonarr(17)
tw = lonarr(10)
mb = lonarr(8)
title = 'STEP 1 of 3: Define Data Type / Range'
; Create a group if one wasn't specified.
;
if (N_ELEMENTS(group) eq 0) then begin
group = widget_base()
groupCreated = 1B
endif else $
groupCreated = 0B
base = widget_base(title=title, /column, xoff=xoff, yoff=yoff, $
/modal, group=group, /tlb_frame_attr)
sb = widget_base(base, /column)
mbs = widget_base(sb)
; -----------------------------------------------
; STEP 1 of 3: Define Data Type / Range
; -----------------------------------------------
mb[0]= widget_base(mbs, /column, event_pro='at_1_event')
sb = widget_base(mb[0], /column, /frame)
lab = widget_label(sb, $
value='Choose the field type which best describes your data:')
lab = widget_label(sb, value='')
sb1 = widget_base(sb, /column, /exclusive)
buts[0]= widget_button(sb1, /no_release, uvalue='mode0', $
value=' Fixed Width (fields are aligned in columns)')
buts[1]= widget_button(sb1, /no_release, uvalue='mode1', $
value=' Delimited (commas, whitespace, etc. separate each field)')
sb = widget_base(mb[0], /row, /frame)
lab = widget_label(sb, value=' Comment String to Ignore: ')
tw[9]= widget_text(sb, xs=10, ys=1, /edit, frame=0, /all_events, $
uvalue='comment')
sb = widget_base(mb[0], /row, /frame)
lab = widget_label(sb, value=' Data Starts at Line: ')
tw[7]= widget_text(sb, xs=5, ys=1, /edit, frame=0, /all_events, $
uvalue='text')
; -----------------------------------------------
; STEP 2 of 3: Define Delimiter / Fields
; -----------------------------------------------
mb[1]= widget_base(mbs, /column, map=0, event_pro='at_2_event')
sb = widget_base(mb[1], /column, /frame)
sb1 = widget_base(sb, /row)
lab = widget_label(sb1, value='Number of Fields Per Line: ')
tw[2]= widget_text(sb1, xs=10, ys=1, frame=0, /edit, /all_events, $
uvalue='fields')
mb[3]= widget_base(mb[1], /column, /frame)
lab = widget_label(mb[3], value='Delimiter Between Data Elements:')
sb1 = widget_base(mb[3], /row)
sb2 = widget_base(sb1, row=2, /exclusive)
mb[5]= widget_base(sb1, /row, /align_bottom)
tw[1]= widget_text(mb[5], xs=2, ys=1, /edit, frame=0, $
/all_events, uvalue='user')
buts[6] = widget_button(sb2, value='White Space', uvalue='delimit2', /no_rel)
buts[7] = widget_button(sb2, value='Comma', uvalue='delimit3', /no_rel)
buts[16]= widget_button(sb2, value='Colon', uvalue='delimit4', /no_rel)
buts[5] = widget_button(sb2, value='Semicolon', uvalue='delimit1', /no_rel)
buts[11]= widget_button(sb2, value='Tab', uvalue='delimit0', /no_rel)
buts[8] = widget_button(sb2, value='Other:', uvalue='delimit5', /no_rel)
; -----------------------------------------------
; STEP 3 of 3: Field Specifications
; -----------------------------------------------
mb[2]= widget_base(mbs, /column, map=0, event_pro='at_3_event')
sb = widget_base(mb[2], column=2)
; Field information table widget (upper-left).
;
sb1 = widget_base(sb, /column, /frame)
tw[8]= widget_table(sb1, value=strarr(3,1), alignment=0, scr_xsize=225, $
scr_ysize=150, uvalue='ltable', column_widths=[100,75,50], $
column_labels=['Name','Data Type','Loc'], /ALL_EVENTS, /SCROLL, $
/RESIZEABLE_COLUMNS)
widget_control, tw[8], column_widths=25, use_table_select=[-1,0,-1,0]
sb1 = widget_base(sb, /column, /frame)
sb2 = widget_base(sb1, /row)
lab = widget_label(sb2, value='Name: ')
tw[4]= widget_text(sb2, xs=18, ys=1, frame=0, /edit, /all_events, $
uvalue='name')
sb2 = widget_base(sb1, /row)
dl = widget_droplist(sb2, Title='Type: ', value=dt_str, uvalue='type')
; ("Column" is used for Fixed Width data organization.)
mb[6]= widget_base(sb1, /row)
lab = widget_label(mb[6], value='Column: ')
tw[5]= widget_text(mb[6], xs=3, ys=1, frame=0, /edit, /all_events, $
uvalue='location')
sb2 = widget_base(sb1, /row)
but = widget_button(sb2, value=' Group ', uvalue='group')
buts[14] = widget_button(sb2, value=' UnGroup ', uvalue='ungroup')
but = widget_button(sb2, value=' Ungroup All ', uvalue='ungroup all')
sb = widget_base(mb[2], /row, /frame)
lab = widget_label(sb, value=' Value to Assign to Missing Data: ')
sb1 = widget_base(sb, /row)
sb2 = widget_base(sb1, /row, /exclusive)
buts[2]= widget_button(sb2, value='IEEE NaN ', /no_rel, uvalue='miss0')
buts[3]= widget_button(sb2, value='', /no_rel, uvalue='miss1')
mb[4] = widget_base(sb1, /row)
tw[0]= widget_text(mb[4], xs=7, ys=1, frame=0, /edit, /all_events, $
uvalue='value')
; -----------------------------------------------
; general text widget and <back, next> buttons
;
sb = widget_base(base, /column)
sb1 = widget_base(sb, /row)
slab = widget_label(sb1, value='Selected Text File ')
mb[7]= widget_base(sb1, /row)
widget_control, mb[7], sensitive=(end_reached eq 0)
value = ' Read in Next ' + STRTRIM(STRING(browseLinesUse),2) + ' Lines '
but = widget_button(mb[7], value=value, uvalue='next set', $
event_pro='at_1_event')
str = strarr(1,n_elements(lines))
tw[6]= widget_table(sb, value=str, alignment=0, scr_xsize=500, $
scr_ysize=table_scr_ysize, uvalue='table', column_labels=['Text'], $
column_widths=450, /ALL_EVENTS, /SCROLL, /RESIZEABLE_COLUMNS)
widget_control, tw[6], column_widths=40, use_table_select=[-1,0,-1,0]
sb = widget_base(base, /row, /align_right)
sb1 = widget_base(sb, /row, /frame)
but = widget_button(sb1, value=' Cancel ', uvalue='cancel')
buts[9] = widget_button(sb1, value=' < Back ', uvalue='back')
buts[10]= widget_button(sb1, value=' Next > ', uvalue='next')
buts[13]= widget_button(sb1, value=' Finish ', uvalue='finish')
widget_control, base, /realize
widget_control, tw[8], /input_focus
p_result = ptr_new({accept:0})
; Set up widget state information.
;
data = { $
base: base, $
mb: mb, $
tw: tw, $
buts: buts, $
dl: dl, $
step: 0, $
name: name, $
p_lines: ptr_new(lines), $
p_rlines: ptr_new(/allocate_heap), $
p_num_fields: ptr_new(/allocate_heap), $
p_template: ptr_new(/allocate_heap), $
p_types: ptr_new(/allocate_heap), $
p_fields: ptr_new(), $
p_groups: ptr_new(), $
p_result: p_result, $
mode: 1, $
data_start: 0L, $
lptr: 0, $
delimit: 32b, $
miss_type: 0, $
last_pos: last_pos, $
change: 1, $
end_reached: end_reached, $
comment: '', $
twm: [1,n_elements(lines)], $
tws: [3,1], $
slab: slab, $
browseLines: browseLinesUse $
}
at_set_state, data
widget_control, base, set_uvalue=data, /no_copy
xmanager, 'at_widget', base
if (groupCreated) then $
widget_control, group, /destroy
cancel = ((*p_result).accept eq 0)
if ((*p_result).accept) then template = (*p_result).template $
else template = 0
ptr_free, p_result
return, template
end ; at_widget
; -----------------------------------------------------------------------------
;
; Purpose: Check that the input filename is a string, exists, and appears
; to be ASCII...
;
function at_check_file, fname
catch, error_status
if (error_status ne 0) then begin
if (n_elements(unit) gt 0) then free_lun, unit
return, -3 ; unexpected error reading from file
endif
if (size(fname,/type) ne 7) then return, -1 ; filename isn't a string
openr, unit, fname, error=error, /get_lun
if (error eq 0) then begin
finfo = fstat(unit)
; set non-ascii values in lookup table
;
lut = bytarr(256) + 1b
lut[7:13] = 0b
lut[32:127] = 0b
data = bytarr(32767<finfo.size, /nozero)
readu, unit, data
free_lun, unit
carriage_return = (total(data eq 10b) gt 0 or total(data eq 13b) gt 0)
if (carriage_return eq 0) then return, -4 ; looks like a binary file
non_printable = (total(lut[data]) gt 0)
if (non_printable) then return, -4 $ ; looks like a binary file
else return, 0 ; everything is cool
endif else $
return, -2 ; unable to open file
end ; at_check_file
; -----------------------------------------------------------------------------
;
; Purpose: The main routine.
;
function ascii_template, $
file, $ ; IN: (opt)
BROWSE_LINES=browseLines, $ ; IN: (opt)
GROUP=group, $ ; IN: (opt)
CANCEL=cancel ; OUT: (opt)
; Set to return to caller on error.
;
ON_ERROR, 2
; ON_ERROR, 0
; Set some defaults.
;
cancel = 0
; Set number of lines for browse button.
;
if (N_ELEMENTS(browseLines) ne 0) then browseLinesUse = browseLines $
else browseLinesUse = 50
; If no file specified, use DIALOG_PICKFILE.
;
if (n_elements(file) eq 0) then begin
file = DIALOG_PICKFILE(/MUST_EXIST, GROUP=group)
if (file eq '') then RETURN, 0
endif
; check that the file is readable and appears to be ASCII
;
ret = at_check_file(file)
case ret of
-1: MESSAGE, 'File name must be a string.'
-2: MESSAGE, 'File "' + file + '" not found.'
-3: MESSAGE, 'Error Reading from file "' + file + '"
-4: MESSAGE, 'File "' + file + '" is not an ASCII file.'
else:
endcase
; Put up the GUI.
;
templateWithPtrs = $
at_widget(file, cancel=cancel, BROWSE_LINES=browseLinesUse, GROUP=group)
; If user canceled, return 0.
;
if (cancel) then RETURN, 0
; Restructure template to eliminate pointers and return it.
; (Include a version number for easier processing if modify
; the template definition later.)
;
template = { $
version: 1.0, $
dataStart: templateWithPtrs.record_start_loc, $
delimiter: templateWithPtrs.delimit, $
missingValue: templateWithPtrs.missing_value, $
commentSymbol: templateWithPtrs.comment_symbol, $
fieldCount: *templateWithPtrs.p_num_fields, $
fieldTypes: (*templateWithPtrs.p_fields).type, $
fieldNames: (*templateWithPtrs.p_fields).name, $
fieldLocations: (*templateWithPtrs.p_fields).loc, $
fieldGroups: *templateWithPtrs.p_groups $
}
at_delete_template, templateWithPtrs
RETURN, template
end ; ascii_template
; -----------------------------------------------------------------------------