; $Id: gaussvaredit.pro,v 1.1 2005/08/18 20:37:41 craig_nist Exp $
;
; Copyright (c) 1991-2000, Research Systems, Inc.  All rights reserved.
;   Unauthorized reproduction prohibited.
;+
; NAME:
;   varEDIT
; PURPOSE:
;   This routine provides an editor for any IDL variable.
; CATEGORY:
;   Widgets
; CALLING SEQUENCE:
;   varEDIT, VAR
; INPUTS:
;   VAR = The variable that is to be edited.
; KEYWORD PARAMETERS:
;   NAME = The NAME of the variable.  This keyword is overwritten with the
;       structure name if the variable is a structure.
;   GROUP = The widget ID of the widget that calls varEdit.  When this
;       ID is specified, a death of the caller results in a death of
;       varEdit.
;   X_SCROLL_SIZE = The X_SCROLL_SIZE keyword allows you to set
;       the width of the scrolling viewport in columns.
;       Default is 4.
;   Y_SCROLL_SIZE = The Y_SCROLL_SIZE keyword allows you to set
;       the height of the scrolling viewport in rows.
;       Default is 4.
; OUTPUTS:
;   VAR= The variable that has been edited, or the original if the user
;       selects the "Cancel" button.
; COMMON BLOCKS:
;   None.
; SIDE EFFECTS:
;   Initiates the XManager if it is not already running.
; RESTRICTIONS:
;   None known.
; PROCEDURE:
;   Display a table widget, a "cancel" button, and an "accept" button.
;   If the user clicks "accept", values in the table widget are
;   written to the variable being edited.
;
;   Note: Pointers and Object References are shown as blank cells.  To
;   edit Pointer or Object References, enter valid IDL expressions
;   such as OBJ_NEW('IDLgrModel'), PTR_VALID(123, /CAST), etc.  To
;   leave Ponters and Object References unchanged, leave their
;   corresponding cells blank.
;
; MODIFICATION HISTORY:
;   Written by: Steve Richards, February, 1991
;   Modified: September 96, LP - rewritten with TABLE widget
;   Modified: March 2000, PCS - Added support for new data types
;       such as Unsigned Integer.  Made Pointers and Object References
;       editable.
;
;-

FUNCTION varEdit__n_elements, arg
;
;function varEDIT__N_ELEMENTS: return the number of elements in arg.
;This function is similar to N_ELEMENTS, but counts recursively,
;where applicable.
;
ON_ERROR, 2 ;return to caller on error.

CASE 1 OF
    SIZE(arg, /TYPE) EQ 8: BEGIN    ;structure or array of structures.
        result = 0L

        FOR i=0L,N_ELEMENTS(arg)-1 DO BEGIN
            FOR j=0L,N_TAGS(arg)-1 DO BEGIN
                result = result + varEdit__n_elements((arg[i]).(j))
            END
        END

        RETURN, result
    END

    ELSE: $
        RETURN, N_ELEMENTS(arg)
ENDCASE

END
;------------------------------------------------------------------------------
;   procedure varEdit_event
;------------------------------------------------------------------------------
; This procedure processes the events being sent by the XManager.
;------------------------------------------------------------------------------
PRO varEdit_event, event

COMPILE_OPT hidden
WIDGET_CONTROL, event.id, GET_UVALUE = whichevent
IF N_ELEMENTS(whichevent) EQ 0 THEN RETURN
IF whichevent NE "THEBUTTON" THEN RETURN

CASE event.value OF

    0: BEGIN                                            ;the user chose to
        WIDGET_CONTROL, event.top, /DESTROY     ;return the initial
      END
                             ;variable
    1: BEGIN
        WIDGET_CONTROL, event.top, GET_UVALUE = pEval
        (*pEval).var = (*pEval).orig
        (*pEval).modified = 1                   ;the user chose the
        WIDGET_CONTROL, event.top, SET_UVALUE = pEval
        WIDGET_CONTROL, event.top, /DESTROY     ;return the Original
      END                       ;variable

    2: BEGIN                        ;the user chose accept
        WIDGET_CONTROL, event.top, GET_UVALUE = pEval, /HOURGLASS
        IF (*pEval).usetable THEN BEGIN
            edit_cell = WIDGET_INFO((*pEval).table, /TABLE_EDIT_CELL)
            if edit_cell[0] EQ -1 AND edit_cell[1] EQ -1 then begin
                (*pEval).modified = 1
                WIDGET_CONTROL, (*pEval).table, GET_VALUE = var
                if (SIZE((*pEval).var))[0] EQ 0 then begin
                  (*pEval).var = var[0]
                endif else begin
                  (*pEval).var = TEMPORARY(var)
                endelse
            endif else begin
                tmp = DIALOG_MESSAGE(['Please commit or cancel the edit',$
                                      'before pressing Accept.'])
                RETURN
            endelse
        ENDIF ELSE BEGIN
            i = 0LL
            ;so go ahead and modify the variable
            WIDGET_CONTROL, (*pEval).table, GET_VALUE = var
            WHILE(i LT N_ELEMENTS(var))DO BEGIN
                CASE (*pEval).entries[i].type OF
                    6: assign = '=COMPLEX'
                    9: assign = '=DCOMPLEX'
                    ELSE: assign = '='
                ENDCASE
                IF ((*pEval).entries[i].type EQ 10 AND var[i] NE '') $
                OR ((*pEval).entries[i].type EQ 11 AND var[i] NE '') $
                OR ((*pEval).entries[i].type NE 10 AND $
                    (*pEval).entries[i].type NE 11) $
                THEN BEGIN
                    str = "(*pEval)." + $
                        (*pEval).entries[i].name + $
                        assign + $
                        ((*pEval).entries[i].type EQ 7 ? "var[i]" : var[i])
                    IF NOT EXECUTE(str, 1) THEN BEGIN
                        void = DIALOG_MESSAGE( $
                            [ $
                                'varEdit: error converting "' $
                                    + var[i] $
                                    + '" to ' $
                                    + (*pEval).entries[i].name, $
                                '', $
                                !error_state.msg, $
                                !error_state.sys_msg $
                                ], $
                            /ERROR $
                            )
                        conversion_err = 1
                    ENDIF
                ENDIF
                i = i + 1
            ENDWHILE
        ENDELSE
        IF NOT KEYWORD_SET(conversion_err) THEN BEGIN
            (*pEval).modified = 1
            WIDGET_CONTROL, event.top, /DESTROY
        ENDIF
    END
    ELSE:

ENDCASE

END ;============= end of varEdit event handling routine task =============


;------------------------------------------------------------------------------
;   procedure AddEditEntry
;------------------------------------------------------------------------------
; This procedure adds an entry to the list that contains the variables names
; and the widget id for the edit field corresponding to the variable name.
;------------------------------------------------------------------------------

PRO varEdit__AddEditEntry, $
    entries, $      ; IN/OUT
    n_ents, $       ; IN/OUT
    thename, $      ; IN
    thetype, $      ; IN
    value, $        ; IN
    n_elems         ; IN

COMPILE_OPT hidden

IF(NOT(KEYWORD_SET(entries))) THEN BEGIN
    entries = REPLICATE({entstr, $
        name: thename, $
        value: value, $
        type: thetype $
        }, n_elems)
    n_ents = 1L
ENDIF ELSE BEGIN
    IF (N_ELEMENTS(entries) LE n_ents) THEN BEGIN
        entries = [temporary(entries), REPLICATE({entstr}, n_elems > 100)]
    END
    entries[n_ents].name = thename
    entries[n_ents].value = value
    entries[n_ents].type = thetype
    n_ents = n_ents + 1
ENDELSE
END ;============== end of varEdit event handling routine task ===============


;------------------------------------------------------------------------------
;   procedure varEditField
;------------------------------------------------------------------------------
;  This routine is used to create the widget or widgets needed for a given
;  variable type.  It could call itself recursively if the variable was itself
;  a structure comprised of other IDL variables.
;------------------------------------------------------------------------------

FUNCTION varEditField, $
    base, $     ; IN
    val, $      ; IN
    usetable, $ ; OUT
    entries, $  ; IN/OUT
    nentries, $ ; IN/OUT
    TOTAL_N_ELEMENTS = total_n_elements, $  ; IN/OUT (opt)
    NAME = NAME, $
    RECNAME = RECNAME, $
    X_SCROLL_SIZE = X_SCROLL_SIZE, $
    Y_SCROLL_SIZE = Y_SCROLL_SIZE

COMPILE_OPT hidden
FORWARD_FUNCTION varEditField

typarr = [ $
    "Undefined", $                  ; 0
    "Byte", $                       ; 1
    "Integer", $                    ; 2
    "Longword Integer", $           ; 3
    "Floating Point", $             ; 4
    "Double Precision Floating", $  ; 5
    "Complex Floating", $           ; 6
    "String", $                     ; 7
    "Structure", $                  ; 8
    "Double Precision Complex", $   ; 9
    "Pointer", $                    ; 10
    "Object Reference", $           ; 11
    "Unsigned Integer", $           ; 12
    "Unsigned Longword Integer", $  ; 13
    '64-bit Integer', $             ; 14
    "Unsigned 64-bit Integer" $     ; 15
    ]

varsize = size(val)
vardims = N_ELEMENTS(varsize) - 2
type = varsize[vardims]
numelements = varsize[vardims + 1]

usetable = 0
IF (NOT(KEYWORD_SET(RECNAME)) $
AND (varsize[0] EQ 1 OR varsize[0] EQ 2)) THEN BEGIN
    IF(type EQ 8) THEN BEGIN
        IF varsize[0] EQ 1 THEN BEGIN
            IF !VERSION.OS_FAMILY EQ 'Windows' AND N_TAGS(val) GT 200 THEN $
                Goto, Cplx_Struct
            FOR i = 0, N_TAGS(val) - 1 DO BEGIN
                strsize = size(val.(i))
                strdims = N_ELEMENTS(strsize) - 2
                IF strsize[strdims] EQ 8 $  ; Structure
                OR strsize[strdims] EQ 10 $ ; Pointer
                OR strsize[strdims] EQ 11 $ ; Object Reference
                OR strsize[strdims + 1] NE varsize[vardims + 1] THEN $
                    Goto, Cplx_Struct
            ENDFOR
            usetable = 1
        ENDIF
    ENDIF ELSE BEGIN
        IF !VERSION.OS_FAMILY EQ 'Windows' AND varsize[1] GT 200 THEN $
            Goto, Cplx_Struct
        usetable = 1
    ENDELSE
ENDIF
Cplx_Struct:
recurse = KEYWORD_SET(RECNAME)

IF (NOT recurse) THEN $
  abase = WIDGET_BASE(base, /FRAME, /COLUMN, XPAD = 8, YPAD = 8)

IF(numelements GT 1) THEN BEGIN             ;if the variable is an
  suffix = " Array("                        ;array, then say so and
  FOR j = 1, varsize[0] DO BEGIN            ;show the array
    suffix = suffix + strtrim(varsize[j], 2)        ;dimensions.
    IF j NE varsize[0] THEN suffix = suffix + ", "
  ENDFOR
  suffix = suffix + ")"
ENDIF ELSE suffix = ""

ztitles=strarr(3)
ztitles=['Name','Mass','iXS']
IF(type EQ 8) THEN NAME = TAG_NAMES(val, /STRUCTURE) ;if the variable is a
                            ;structure, use its
                            ;name

;build up the name of variable with the type in parentheses
IF(NOT recurse) THEN BEGIN
    IF(KEYWORD_SET(NAME)) THEN $
      lbl = WIDGET_LABEL(abase, $
                         VALUE = NAME + " (" + typarr[type] + suffix + ")") $
    ELSE lbl = WIDGET_LABEL(abase, $
                            value = typarr[type] + suffix)
ENDIF

IF(NOT(KEYWORD_SET(RECNAME))) THEN BEGIN
    RECNAME = 'var'         ;establish the name
                            ;if not being called
                            ;recursively
END

IF(N_ELEMENTS(X_SCROLL_SIZE) EQ 0) THEN $
  XSCROLL_SIZE = 4 ELSE XSCROLL_SIZE = X_SCROLL_SIZE
IF(N_ELEMENTS(Y_SCROLL_SIZE) EQ 0) THEN $
  YSCROLL_SIZE = 4 ELSE YSCROLL_SIZE = Y_SCROLL_SIZE

IF (usetable) THEN BEGIN
    IF(type EQ 8) THEN BEGIN
        column_labels = TAG_NAMES(val)
        RETURN, WIDGET_TABLE( $
            abase, $
            value = val, $
            COLUMN_LABELS = column_labels, $
            /RESIZEABLE_COLUMNS, $
            /EDIT, $
            X_SCROLL_SIZE = XSCROLL_SIZE, $
            Y_SCROLL_SIZE = YSCROLL_SIZE $
            )
    ENDIF ELSE BEGIN
        RETURN, WIDGET_TABLE( $
            abase, $
            value = val, $
            /RESIZEABLE_COLUMNS, $
            /EDIT, $
            X_SCROLL_SIZE = XSCROLL_SIZE, $
            Y_SCROLL_SIZE = YSCROLL_SIZE $
            )
    ENDELSE
ENDIF

IF(varsize[0] GT 1) THEN BEGIN
  moduli = LONARR(varsize[0]-1) + 1
  FOR i = varsize[0], 2,-1 DO BEGIN
    FOR j = 1,i-1 DO $
      moduli[i - 2] = moduli[i - 2] * varsize[j]
  ENDFOR
ENDIF

IF N_ELEMENTS(total_n_elements) EQ 0 THEN BEGIN
    total_n_elements = varEdit__n_elements(val)
END

FOR element = 0L, numelements - 1 DO BEGIN       ;for each array element

  IF(numelements NE 1) THEN BEGIN           ;use array subscripting
    indexname = "("
    indexname = indexname + $
        strtrim((element mod varsize[1]),2)
    IF(varsize[0] GT 1) THEN BEGIN
      indexarr = lonarr(varsize[0] - 1)
      flatindex = element
      FOR i = varsize[0] - 2, 0, -1 DO BEGIN
    indexarr[i] = flatindex / moduli[i]
    flatindex = flatindex mod moduli[i]
      ENDFOR
      FOR i = 0, varsize[0] - 2 DO $
    indexname = indexname + ", " + $
        strtrim(indexarr[i], 2)
    ENDIF
    indexname = indexname + ")"
    thename = RECNAME + indexname
  ENDIF ELSE BEGIN
    thename = RECNAME
  ENDELSE

  ;depending on the type, build a string variable with proper formatting
  CASE type OF
    0: thevalue = "Undefined Variable"          ;Undefined

    1: thevalue = string(val[element], $        ;Byte
        FORMAT = '(I3)')

    7: thevalue = val[element]              ;String

    8: BEGIN                        ;Structure
        tags = TAG_NAMES(val[element])
        FOR i = 0, N_ELEMENTS(tags) - 1 DO BEGIN
            error = EXECUTE("fieldvalue = val[element]." + tags[i])
            fldsize = size(fieldvalue)
            flddims = N_ELEMENTS(fldsize) - 2
            id = varEditField( $
                abase, $
                fieldvalue, $
                usetable, $
                entries, $
                nentries, $
                TOTAL_N_ELEMENTS = total_n_elements, $
                NAME = tags[i], $
                RECNAME = thename + "." + tags[i], $
                X_SCROLL_SIZE = XSCROLL_SIZE, $
                Y_SCROLL_SIZE = YSCROLL_SIZE $
                )
        ENDFOR
    END
    10: thevalue = ''
    11: thevalue = ''
    ELSE: thevalue = strtrim(val[element], 2)
  ENDCASE

  IF(type NE 8) THEN BEGIN
    varEdit__AddEditEntry, $
        entries, $
        nentries, $
        thename, $
        type, $
        thevalue, $
        total_n_elements
  END

ENDFOR

table = 0
IF (NOT recurse) THEN BEGIN
    IF (N_ELEMENTS(entries.value) GT 1) THEN BEGIN
        table = WIDGET_TABLE( $
            abase, $
            VALUE = TRANSPOSE((entries.value)[0:nentries-1]), $
            ROW_LABELS = TRANSPOSE((entries.name)[0:nentries-1]), $
            COLUMN_LABELS = '', $
            /RESIZEABLE_COLUMNS, $
            /EDIT, $
            COLUMN_WIDTHS = 150, $
            Y_SCROLL_SIZE = YSCROLL_SIZE $
            )
    ENDIF ELSE BEGIN
        table = WIDGET_TABLE( $
            abase, $
            VALUE = [entries.value], $
            ROW_LABELS = [entries.name], $
            COLUMN_LABELS = '', $
            /RESIZEABLE_COLUMNS, $
            /EDIT, $
            COLUMN_WIDTHS=150, $
            Y_SCROLL_SIZE = YSCROLL_SIZE $
            )
    ENDELSE
ENDIF

return, table
END ;============= end of varEdit event handling routine task =============


;------------------------------------------------------------------------------
;   procedure varEdit
;------------------------------------------------------------------------------
; this is the actual routine that is called.  It builds up the variable editing
; fields by calling other support routines and then registers the widget
; heiarchy with the XManager.  Notice that the widget is registered as a MODAL
; widget so it will desensitize all other current widgets until it is done.
;------------------------------------------------------------------------------
PRO gaussvarEdit, var, orig,changed ,GROUP = GROUP, NAME = NAME, $
              X_SCROLL_SIZE = X_SCROLL_SIZE, Y_SCROLL_SIZE = Y_SCROLL_SIZE

on_error, 2 ; Return to caller on error.

if(n_params() ne 3) THEN $
  MESSAGE, "Must have one parameter"

if n_elements(var) eq 0 then $
  MESSAGE, 'Argument is undefined.'

; Create parent of modal base if needed
if (N_ELEMENTS(GROUP) NE 1) then begin
  GROUP_ID = WIDGET_BASE(MAP=0)
endif else begin
  GROUP_ID = GROUP
endelse

varEditbase = WIDGET_BASE(TITLE = "varEdit", $    ;create the main base
        /COLUMN, GROUP_LEADER=GROUP_ID, /MODAL)

menu = Cw_Bgroup( $
    varEditbase, $
    ['Cancel', 'Restore', 'Accept' ], $
    /ROW, $
    IDS=IDS, $
    UVALUE="THEBUTTON" $
    )

if not arg_present(var) then begin
    widget_control, ids[1], sensitive=0
end

WIDGET_CONTROL, /HOURGLASS
entries = 0
nentries = 0
table = varEditField(varEditbase, var, usetable, entries, nentries, $
                      NAME = NAME, X_SCROLL_SIZE = X_SCROLL_SIZE, $
                      Y_SCROLL_SIZE = Y_SCROLL_SIZE)

varEditStat = {var:var, $
                orig:orig,$
                entries:entries, $
                modified:0, $
                table: table, $
                usetable: usetable}
pvarEditStat = PTR_NEW(varEditStat, /NO_COPY)
WIDGET_CONTROL, varEditbase, SET_UVALUE=pvarEditStat

WIDGET_CONTROL, varEditbase, /REALIZE

XManager, "varEdit", varEditbase

; Get the return value
IF ((*pvarEditStat).modified) THEN begin
  var = (*pvarEditStat).var
  changed=((*pvarEditStat).modified)
end
PTR_FREE, pvarEditStat

; Destroy parent of modal base
if (N_ELEMENTS(GROUP) NE 1) then begin
  WIDGET_CONTROL, GROUP_ID, /DESTROY
endif

END ;================== end of varEdit main routine =======================
