;+
; NAME:
;   CW_FIELD
;
; PURPOSE:
;
;   The purpose of this compound widget is to provide an alternative
;   to the CW_FIELD widget offered in the IDL distribution. What has
;   always annoyed me about CW_FIELD is that the text widgets do not
;   look editable to the users on Windows platforms. This program
;   corrects that deficiency and adds some features that I think
;   would be helpful. For example, you can now assign an event handler
;   to the compound widget.
;
; AUTHOR:
;   FANNING SOFTWARE CONSULTING
;   David Fanning, Ph.D.
;   2642 Bradbury Court
;   Fort Collins, CO 80521 USA
;   Phone: 970-221-0438
;   E-mail: davidf@dfanning.com
;   Coyote's Guide to IDL Programming: http://www.dfanning.com/
;
; CATEGORY:
;
;   General programming.
;
; CALLING SEQUENCE:
;
;   fieldID = Cw_Field(parent, Title='X Size: ", Value=256, /IntegerValue)
;
; INPUT PARAMETERS:
;
;   parent -- The parent widget ID of the compound widget. Required.
;
; INPUT KEYWORDS:
;
;   Column -- Set this keyword to have the Label Widget above the Text Widget.
;   CR_Only -- Set this keyword if you only want Carriage Return events. If
;              this keyword is not set, all events are returned. No events
;              are returned unless the EVENT_PRO or EVENT_FUNC keywords are used.
;   DoubleValue -- Set this keyword if you want DOUBLE values returned.
;   Decimal -- Set this keyword to the number of digits to the right of the decimal
;              point in FLOATVALUE and DOUBLEVALUE numbers.
;   Digits -- Set this keyword to the number of digits permitted in INTERGERVALUE and LONGVALUE numbers.
;   Event_Func -- Set this keyword to the name of an Event Function. If this
;                 keyword is undefined and the Event_Pro keyword is undefined,
;                 all compound widget events are handled internally and not
;                 passed on to the parent widget.
;   Event_Pro -- Set this keyword to the name of an Event Procedure. If this
;                keyword is undefined and the Event_Func keyword is undefined,
;                all compound widget events are handled internally and not
;                passed on to the parent widget.
;   FieldFont -- The font name for the text in the Text Widget.
;   FloatValue -- Set this keyword for FLOAT values.
;   Frame -- Set this keyword to put a frame around the compound widget.
;   IntegerValue -- Set this keyword for INTEGER values.
;   LabelFont -- The font name for the text in the Label Widget.
;   LabelSize -- The X screen size of the Label Widget.
;   LongValue -- Set this keyword for LONG values.
;   Row=row -- Set this keyword to have the Label beside the Text Widget. (The default.)
;   Scr_XSize -- The X screen size of the compound widget.
;   Scr_YSize -- The Y screen size of the compound widget.
;   StringValue -- Set this keyword for STRING values. (The default.)
;   Title -- The text to go on the Label Widget.
;   UValue -- A user value for any purpose.
;   Value -- The "value" of the compound widget.
;   XSize -- The X size of the Text Widget.
;   YSize -- The Y size of the Text Widget.
;   classic -- added by GD, 2025, "classic" events returned.
;
; COMMON BLOCKS:
;
;   None.
;
; RESTRICTIONS:
;
;   None.
;
; EVENT STRUCTURE:
;
;   All events are handled internally unless either the Event_Pro or Event_Func
;   keywords are used to assign an event handler to the compound widget. By
;   default all events generated by the text widget are passed to the assigned
;   event handler. If you wish to receive only Carriage Return events, set the
;   CR_Only keyword.
;
;   event = { DF_CW_FIELD, $      ; The name of the event structure.
;             ID: 0L, $            ; The ID of the compound widget's top-level base.
;             TOP: 0L, $           ; The widget ID of the top-level base of the hierarchy.
;             HANDLER: 0L, $       ; The event handler ID. Filled out by IDL.
;             Value: Ptr_New(), $  ; A pointer to the widget value.
;             Type:""              ; A string indicating the type of data in the VALUE field.
;           }                      ; Values are "INT", "LONG", "FLOAT", "DOUBLE", or "STRING".
;
; EXAMPLE:
;
;   An example program is provided at the end of the DF_CW_FIELD code. To run it,
;   type this commands:
;
;      IDL> .run Df_Cw_Field
;
; MODIFICATION HISTORY:
;
;   Written by: David Fanning, 17 NOV 1999.
;   Added check to make float and double values finite. 18 NOV 1999. DWF.
;   Fixed a bug when selecting and deleting all numerical text. 19 NOV 1999. DWF.
;   Added DECIMAL and DIGITS keywords. 2 Jan 2000. DWF.
;   Added the POSITIVE keyword. 12 Jan 2000. DWF.
;   Fixed a few minor bugs with delete and digits. 12 Jan 2000. DWF.
;   Made GET_VALUE function return pointer to data, instead of data. 12 Jan 2000. DWF.
;   Fixed a small typo: "aveDecimal" to "haveDecimal". 10 March 2000. DWF.
;   Added to GDL pro suite to provide "cw_field", 2025 Gilles Duvert, while preserving
;   as uch as possible the original work by D. Fanning
;-
;
;###########################################################################
;
; LICENSE
;
; This software is OSI Certified Open Source Software.
; OSI Certified is a certification mark of the Open Source Initiative.
;
; Copyright © 2000 Fanning Software Consulting.
;
; This software is provided "as-is", without any express or
; implied warranty. In no event will the authors be held liable
; for any damages arising from the use of this software.
;
; Permission is granted to anyone to use this software for any
; purpose, including commercial applications, and to alter it and
; redistribute it freely, subject to the following restrictions:
;
; 1. The origin of this software must not be misrepresented; you must
;    not claim you wrote the original software. If you use this software
;    in a product, an acknowledgment in the product documentation
;    would be appreciated, but is not required.
;
; 2. Altered source versions must be plainly marked as such, and must
;    not be misrepresented as being the original software.
;
; 3. This notice may not be removed or altered from any source distribution.
;
; For more information on Open Source Software, visit the Open Source
; web site: http://www.opensource.org.
;
;###########################################################################


Function Df_Cw_Field_ReturnValue, inputValue, dataType

; This utility routine takes a string and turns it into a number,
; depending upon the required data type.

ON_IOERROR, CatchIt

IF (Byte(inputValue))[0] EQ 32B THEN RETURN, ""

CASE dataType OF
   'INT': IF inputValue EQ "" OR inputValue EQ "-" OR inputValue EQ "+" THEN $
      retValue = 'NULLVALUE' ELSE retValue = Fix(inputValue)
   'LONG': IF inputValue EQ "" OR inputValue EQ "-" OR inputValue EQ "+" THEN $
      retValue = 'NULLVALUE' ELSE retValue = Long(inputValue)
   'FLOAT' : IF inputValue EQ "" OR inputValue EQ "-" OR inputValue EQ "+" THEN $
      retValue = 'NULLVALUE' ELSE retValue = Float(inputValue)
   'DOUBLE': IF inputValue EQ "" OR inputValue EQ "-" OR inputValue EQ "+" THEN $
      retValue = 'NULLVALUE' ELSE retValue = Double(inputValue)
   'STRING' : retValue = inputValue
ENDCASE
RETURN, retValue

CatchIt:
   retValue = 'NULLVALUE'
   RETURN, retValue
END ;----------------------------------------------------------------------------



Function Df_Cw_Field_Validate, value, dataType, Decimal=decimal, Digits=digits, $
   Positive=positive

; This function eliminates illegal characters from a string that represents
; a number. The return value is a properly formatted string that can be turned into
; an INT, LONG, FLOAT, or DOUBLE value.
;
; + 43B
; - 45B
; . 46B
; 0 - 9 48B -57B
; 'eEdD' [101B, 69B, 100B, 68B]

   ; A null string should be returned at once.

IF N_Elements(value) EQ 0 THEN value = ""
value = value[0]
IF value EQ "" THEN RETURN, String(value)

   ; No leading or trainnig blank characters to evaluate.

value = StrTrim(value, 2)

IF N_Elements(dataType) EQ 0 THEN dataType = 'STRING'

   ; A string value should be returned at once. Nothing to check.

IF StrUpCase(dataType) EQ 'STRING' THEN RETURN, value

   ; Check integers and longs. A "-" or "+" in the first character is allowed. Otherwise,
   ; only number between 0 and 9, or 43B to 57B.

IF StrUpCase(dataType) EQ 'INT' OR StrUpCase(dataType) EQ 'LONG' THEN BEGIN

   returnValue = Ptr_New(/Allocate_Heap)
   asBytes = Byte(value)
   IF positive THEN BEGIN
      IF (asBytes[0] EQ 43B) OR $
         (asBytes[0] GE 48B AND asBytes[0] LE 57B) THEN *returnValue = [asBytes[0]]
   ENDIF ELSE BEGIN
      IF (asBytes[0] EQ 45B) OR (asBytes[0] EQ 43B) OR $
         (asBytes[0] GE 48B AND asBytes[0] LE 57B) THEN *returnValue = [asBytes[0]]
   ENDELSE
   length = StrLen(asBytes)
   IF length EQ 1 THEN BEGIN
      IF N_Elements(*returnValue) EQ 0 THEN  *returnValue = [32B] ELSE $
            *returnValue = [asBytes[0]]
   ENDIF ELSE BEGIN
      FOR j=1,length-1 DO BEGIN
         IF (asBytes[j] GE 48B AND asBytes[j] LE 57B) THEN BEGIN
            IF N_Elements(*returnValue) EQ 0 THEN  *returnValue = [asBytes[j]] ELSE $
               *returnValue = [*returnValue, asBytes[j]]
         ENDIF
      ENDFOR
  ENDELSE
  IF N_Elements(*returnValue) NE 0 THEN retValue = String(*returnValue) ELSE retValue = ""
  Ptr_Free, returnValue

      ; Check for digit restrictions.

  IF digits GT 0 THEN BEGIN
      retValue = StrTrim(retValue, 2)
      IF StrMid(retValue, 0, 1) EQ "-" THEN digits = digits + 1
      retValue = StrMid(retValue, 0, digits)
  ENDIF

  RETURN, retValue

ENDIF

   ; Check floating and double values. (+,-) in first character or after 'eEdD'.
   ; Only numbers, signs, decimal points, and 'eEdD' allowed.

IF StrUpCase(dataType) EQ 'FLOAT' OR StrUpCase(dataType) EQ 'DOUBLE' THEN BEGIN
   returnValue = Ptr_New(/Allocate_Heap)
   asBytes = Byte(value)
   IF positive THEN BEGIN
      IF (asBytes[0] EQ 43B) OR $
         (asBytes[0] GE 48B AND asBytes[0] LE 57B) OR $
         (asBytes[0] EQ 46B) THEN *returnValue = [asBytes[0]]
      IF (asBytes[0] EQ 46B) THEN haveDecimal = 1 ELSE haveDecimal = 0
   ENDIF ELSE BEGIN
      IF (asBytes[0] EQ 45B) OR (asBytes[0] EQ 43B) OR $
         (asBytes[0] GE 48B AND asBytes[0] LE 57B) OR $
         (asBytes[0] EQ 46B) THEN *returnValue = [asBytes[0]]
      IF (asBytes[0] EQ 46B) THEN haveDecimal = 1 ELSE haveDecimal = 0
   ENDELSE
   haveExponent = 0
   length = StrLen(asBytes)
   prevByte = asBytes[0]
   exponents = Byte('eEdD')
   IF length EQ 1 THEN BEGIN
      IF N_Elements(*returnValue) EQ 0 THEN  *returnValue = [32B] ELSE $
            *returnValue = [asBytes[0]]
   ENDIF ELSE BEGIN
      FOR j=1,length-1 DO BEGIN
         IF (asBytes[j] GE 48B AND asBytes[j] LE 57B) THEN BEGIN
            IF N_Elements(*returnValue) EQ 0 THEN  *returnValue = [asBytes[j]] ELSE $
               *returnValue = [*returnValue, asBytes[j]]
            prevByte = asBytes[j]
         ENDIF ELSE BEGIN

            ; What kind of thing is it?

            IF (asBytes[j] EQ 46B) THEN BEGIN ; A decimal point.
               IF haveDecimal EQ 0 THEN BEGIN
                  *returnValue = [*returnValue, asBytes[j]]
                  haveDecimal = 1
                  prevByte = asBytes[j]
               ENDIF
            ENDIF

            IF (asBytes[j] EQ 45B) OR (asBytes[j] EQ 43B) THEN BEGIN ; A + or - sign.
               index = Where(exponents EQ prevByte, count)
               IF count EQ 1 AND haveExponent THEN BEGIN
                  *returnValue = [*returnValue, asBytes[j]]
                  haveDecimal = 1
                  prevByte = asBytes[j]
               ENDIF
            ENDIF

            index = Where(exponents EQ asBytes[j], count)
            IF count EQ 1 AND haveExponent EQ 0 THEN BEGIN ; An exponent
               *returnValue = [*returnValue, asBytes[j]]
               haveExponent = 1
               prevByte = asBytes[j]
            ENDIF
         ENDELSE
      ENDFOR
   ENDELSE
   IF N_Elements(*returnValue) NE 0 THEN BEGIN

      retValue = String(*returnValue)
      retValue = StrTrim(retValue, 2)

               ; Check for decimal restrictions

      IF decimal GE 0 THEN BEGIN
         theDecimalPt = StrPos(retValue, '.')
         IF theDecimalPt NE -1 THEN retValue = StrMid(retValue, 0, theDecimalPt + decimal + 1)
      ENDIF

   ENDIF ELSE retValue = ""
   Ptr_Free, returnValue

      ; Is this a representable number?

   testValue = Df_Cw_Field_ReturnValue(retValue, dataType)
   IF String(testValue) NE 'NULLVALUE' THEN numCheck = Finite(testValue) ELSE numCheck = 1
   IF numCheck THEN BEGIN
      RETURN, retValue
   ENDIF ELSE BEGIN
      Message, 'The requested number is not representable.', /Informational
      RETURN, ""
   ENDELSE
ENDIF

END ;----------------------------------------------------------------------------



Pro Df_Cw_Field__Define

; The Df_Cw_Field Event Structure.

   event = { DF_CW_FIELD, $      ; The name of the event structure.
             ID: 0L, $            ; The ID of the compound widget's top-level base.
             TOP: 0L, $           ; The widget ID of the top-level base of the hierarchy.
             HANDLER: 0L, $       ; The event handler ID. Filled out by IDL.
             Value: Ptr_New(), $  ; A pointer to the widget value.
             Type:"" $            ; A string indicating the type of data in the VALUE field.
           }                      ; Values are "INT", "LONG", "FLOAT", "DOUBLE", or "STRING".

END ;----------------------------------------------------------------------------



Pro Df_Cw_Field_Kill_Notify, ID

; This routine cleans up the pointer when the compound widget is destroyed.

Widget_Control, ID, Get_UValue=info, /No_Copy
Ptr_Free, info.theValue
END ;----------------------------------------------------------------------------



Pro Df_Cw_Field_Set_Value, cw_tlb, value

; This procedure sets a value for the compound widget. The value
; is a value appropriate for the data type or a string.

   ; Get info structure.

info_carrier = Widget_Info(cw_tlb, Find_by_UName='INFO_CARRIER')
Widget_Control, info_carrier, Get_UValue=info, /No_Copy

   ; Validate the value.

theText = Strtrim(value, 2)
theText = Df_Cw_Field_Validate(theText, info.dataType, Decimal=info.decimal, $
   Digits=info.digits, Positive=info.positive)

   ; Load the value in the widget.

Widget_Control, info.textID, Set_Value=theText, Set_Text_Select=[StrLen(theText),0]
info.theText = theText

   ; Set the actual value of the compound widget.

*info.theValue = Df_Cw_Field_ReturnValue(info.theText, info.dataType)

Widget_Control, info_carrier, Set_UValue=info, /No_Copy
END ;----------------------------------------------------------------------------

; added by GD, 2025 for GDL
Function Cw_Field_Get_Value, cw_tlb

; This function returns the numerical or string value of the
; compound widget.

info_carrier = Widget_Info(cw_tlb, Find_by_UName='INFO_CARRIER')
Widget_Control, info_carrier, Get_UValue=info, /No_Copy
value = info.theValue
Widget_Control, info_carrier, Set_UValue=info, /No_Copy
RETURN, (*value)
END ;----------------------------------------------------------------------------


Function Df_Cw_Field_Get_Value, cw_tlb

; This function returns the numerical or string value of the
; compound widget.

info_carrier = Widget_Info(cw_tlb, Find_by_UName='INFO_CARRIER')
Widget_Control, info_carrier, Get_UValue=info, /No_Copy
value = info.theValue
Widget_Control, info_carrier, Set_UValue=info, /No_Copy
RETURN, value
END ;----------------------------------------------------------------------------




PRO Df_Cw_Field_Event_Handler, event

; The main event handler for the compound widget.

   ; Get the info structure. Get the previous text, the current
   ; cursor location in the text widget, and indicate this is not
   ; a Carriage Return event.

Widget_Control, event.ID, Get_UValue=info, /No_Copy
previousText = info.theText
textLocation = Widget_Info(event.id, /Text_Select)
cr_event = 0

   ; What kind of event is this?

possibleTypes = ['INSERT SINGLE CHARACTER', 'INSERT MULTIPLE CHARACTERS', 'DELETE TEXT', 'SELECT TEXT']
thisType = possibleTypes[event.type]

   ; Branch on event type.

CASE thisType OF

   'INSERT SINGLE CHARACTER': BEGIN

            ; Get the current contents of text widget. Validate it.

         Widget_Control, info.textID, Get_Value=newText
         newText = newText[0]
         validText = Df_Cw_Field_Validate(newText, info.dataType, Decimal=info.decimal, $
               Digits=info.digits, Positive=info.positive)

            ; If it is valid, leave it alone. If not, go back to previous text.

         IF validText NE newText THEN BEGIN
            Widget_Control, info.textID, Set_Value=previousText, Set_Text_Select=[textLocation[0]-1,0]
         ENDIF ELSE BEGIN
            info.theText = validText
            testValue  = Df_Cw_Field_ReturnValue(validText, info.dataType)
            IF String(testValue) EQ "NULLVALUE" THEN BEGIN
               Ptr_Free, info.theValue
               info.theValue = Ptr_New(/Allocate_Heap)
            ENDIF ELSE *info.theValue = testValue
         ENDELSE
            ; Is this a Carriage Return event?

         IF event.ch EQ 10B then cr_event = 1
      ENDCASE

   'INSERT MULTIPLE CHARACTERS': BEGIN

            ; Same thing as above, but for all the characters you are inserting.

         Widget_Control, info.textID, Get_Value=newText
         newText = newText[0]
         validText = Df_Cw_Field_Validate(newText, info.dataType, Decimal=info.decimal, $
            Digits=info.digits, Positive=info.positive)
         IF validText NE newText THEN BEGIN
            Widget_Control, info.textID, Set_Value=previousText, Set_Text_Select=[textLocation[0]-1,0]
         ENDIF ELSE BEGIN
            info.theText = validText
            testValue  = Df_Cw_Field_ReturnValue(validText, info.dataType)
            IF String(testValue) EQ "NULLVALUE" THEN BEGIN
               Ptr_Free, info.theValue
               info.theValue = Ptr_New(/Allocate_Heap)
            ENDIF ELSE *info.theValue = testValue
         ENDELSE
      ENDCASE

   'DELETE TEXT': BEGIN

            ; Just get the new text and update the info stucture.

         Widget_Control, info.textID, Get_Value=theText
         theText = theText[0]
         validText = Df_Cw_Field_Validate(theText, info.dataType, Decimal=info.decimal, $
            Digits=info.digits, Positive=info.positive)

           ; Load the valid text.

        Widget_Control, info.textID, Set_Value=validText, Set_Text_Select=[textLocation[0],0]
        info.theText = validText
        testValue  = Df_Cw_Field_ReturnValue(info.theText, info.dataType)
        IF String(testValue) EQ "NULLVALUE" THEN BEGIN
            Ptr_Free, info.theValue
            info.theValue = Ptr_New(/Allocate_Heap)
        ENDIF ELSE *info.theValue = testValue
      ENDCASE

   'SELECT TEXT': ; Nothing to do.

ENDCASE

   ; Do you report all events, or only Carriage Return events?

if info.classic then begin
   val=(*info.thevalue)
   dtype=0
   case info.datatype  of
      'STRING':dtype=0
      'INT':dtype=2
      'LONG':dtype=3
      'FLOAT':dtype=1
      'DOUBLE':dtype=5
   endcase
   if (info.positive) then begin
      dtype=4
      val=fix(val,/ULONG)
   endif
   if isa(val,/NULL) then val=0
   thisEvent = {id:info.cw_tlb, top:event.top, handler:0L, value:val, type:dtype, update:cr_event eq 1}
endif else thisEvent = {Df_Cw_Field, info.cw_tlb, event.top, 0L, info.theValue, info.dataType}

IF info.cr_only THEN BEGIN
;   IF info.event_func NE "" THEN BEGIN
      IF cr_event THEN Widget_Control, info.cw_tlb, Send_Event=thisEvent
;   ENDIF
;   IF info.event_pro NE "" THEN BEGIN
;      IF cr_event THEN Widget_Control, info.cw_tlb, Send_Event=thisEvent
;   ENDIF
ENDIF ELSE BEGIN
;   IF info.event_func NE "" THEN BEGIN
      Widget_Control, info.cw_tlb, Send_Event=thisEvent
;   ENDIF
;
;   IF info.event_pro NE "" THEN BEGIN
;      Widget_Control, info.cw_tlb, Send_Event=thisEvent
;   ENDIF
ENDELSE

   ; Out of here.

Widget_Control, event.ID, Set_UValue=info, /No_Copy
END ;----------------------------------------------------------------------------



Function Df_Cw_Field, $          ; The compound widget Df_Cw_Field.
   parent, $                      ; The parent widget. Required for all compound widgets.
   Column=column, $               ; Set this keyword to have Label above Text Widget.
   CR_Only=cr_only, $             ; Set this keyword if you only want Carriage Return events.
   Digits=digits, $               ; Set this keyword to number of allowed digits in INT and LONG values.
   Decimal=decimal, $             ; Set to the number of digits to right of decimal point.
   DoubleValue=doublevalue, $     ; Set this keyword if you want DOUBLE values returned.
   Event_Func=event_func, $       ; Set this keyword to the name of an Event Function.
   Event_Pro=event_pro, $         ; Set this keyword to the name of an Event Procedure.
   FieldFont=fieldfont, $         ; The font name for the text in the Text Widget.
   FloatValue=floatvalue, $       ; Set this keyword for FLOAT values.
   Frame=frame, $                 ; Set this keyword to put a frame around the compound widget.
   IntegerValue=integervalue, $   ; Set this keyword for INTEGER values.
   LabelFont=labelfont, $         ; The fon name for the text in the Label Widget.
   LabelSize=labelsize, $         ; The X screen size of the Label Widget.
   LongValue=longvalue, $         ; Set this keyword for LONG values.
   Positive=positive, $           ; Set this keyword to only allow positive number values.
   Row=row, $                     ; Set this keyword to have the Label beside the Text Widget. (The default.)
   Scr_XSize=scr_xsize, $         ; The X screen size of the compound widget.
   Scr_YSize=scr_ysize, $         ; The Y screen size of the compound widget.
   StringValue=stringvalue, $     ; Set this keyword for STRING values. (The default.)
   Title=title, $                 ; The text to go on the Label Widget.
   UValue=uvalue, $               ; A user value for any purpose.
   Value=value, $                 ; The "value" of the compound widget.
   XSize=xsize,$                    ; The X size of the Text Widget.
   YSize=ysize,$                    ; The X size of the Text Widget.
   textID=textID, $
   floating=floating,$
   noedit=noedit,$
   classic=classic

   ; A parent is required.

IF N_Elements(parent) EQ 0 THEN BEGIN
   Message, 'A PARENT argument is required. Returning...', /Informational
   RETURN, -1L
ENDIF

   ; Check keyword values.
IF N_elements(classic) eq 0 then classic = 0 else classic = 1
IF N_elements(noedit) eq 0 then editable = 1 else editable = 0
IF N_Elements(column) EQ 0 THEN column = 0
IF N_Elements(digits) EQ 0 THEN digits = 0 ELSE digits = Fix(digits)
IF N_Elements(decimal) EQ 0 THEN decimal = -1 ELSE decimal = Fix(decimal)
IF N_Elements(event_func) EQ 0 THEN event_func = ""
IF N_Elements(event_pro) EQ 0 THEN event_pro = ""
IF N_Elements(fieldfont) EQ 0 THEN fieldfont = ""
IF N_Elements(frame) EQ 0 THEN frame = 0
IF N_Elements(labelfont) EQ 0 THEN labelfont = ""
IF N_Elements(labelsize) EQ 0 THEN labelsize = 0
IF N_Elements(scr_xsize) EQ 0 THEN scr_xsize = 0
IF N_Elements(scr_ysize) EQ 0 THEN scr_ysize = 0
IF N_Elements(title) EQ 0 THEN title = "Input Value: "
IF N_Elements(uvalue) EQ 0 THEN uvalue = ""
IF N_Elements(value) EQ 0 THEN value = "" ELSE value = StrTrim(value,2)
IF N_Elements(xsize) EQ 0 THEN xsize = 0
IF N_Elements(ysize) EQ 0 THEN ysize = 0
IF N_Elements(row) EQ 0 AND column EQ 0 THEN row = 1 ELSE row = 0
positive = Keyword_Set(positive)

   ; What data type are we looking for?

dataType = 'STRING'
IF Keyword_Set(stringvalue) THEN dataType = 'STRING'
IF Keyword_Set(integervalue) THEN dataType = 'INT'
IF Keyword_Set(longvalue) THEN dataType = 'LONG'
IF Keyword_Set(floatvalue) THEN dataType = 'FLOAT'
IF Keyword_Set(doublevalue) THEN dataType = 'DOUBLE'

   ; Validate the input value.

value = Df_Cw_Field_Validate(value, dataType, Decimal=decimal, Digits=digits, Positive=positive)

   ; Create the widgets.

cw_tlb = Widget_Base( parent, $               ; The top-level base of the compound widget.
   Pro_Set_Value='Df_Cw_Field_Set_Value', $
   Func_Get_Value='Cw_Field_Get_Value', $ ; modified: converts pointer to value
   Frame=frame, $
   Row=row, $
   Column=Keyword_Set(column), $
   Base_Align_Center=1, $
   UValue=uvalue, $
   Event_Pro=event_pro, $
   Event_Func=event_func )

labelID = Widget_Label( cw_tlb, Value=title, Font=labelfont, $ ; The Label Widget.
  Scr_XSize=labelsize)

textID = Widget_Text( cw_tlb, $  ; The Text Widget.
   Value=value, $
   XSize=xsize, $
   YSize=ysize, $
   Scr_XSize=scr_xsize, $
   Scr_YSize=scr_ysize, $
   Font=fieldfont, $
   All_Events=1, $
   Event_Pro='Df_Cw_Field_Event_Handler', $
   UName='INFO_CARRIER', $
   Kill_Notify='Df_Cw_Field_Kill_Notify', $
   Editable=editable )

   ; Set the actual return value of the compound widget.

theValue = Df_Cw_Field_ReturnValue(value, dataType)

   ; The info structure.

info = { theText:value, $                  ; The text in the Text Widget.
         theValue:Ptr_New(theValue), $     ; The real value of the Text Widget.
         cw_tlb:cw_tlb, $                  ; The top-level base of the compound widget.
         event_func:event_func, $          ; The name of the event handler function.
         event_pro:event_pro, $            ; The name of the event handler procedure.
         cr_only:Keyword_Set(cr_only), $   ; A flag to return events only on CR events.
         dataType:dataType, $              ; The type of data wanted in the compound widget.
         decimal:decimal, $                ; The number of digits in decimal numbers.
         digits:digits, $                  ; The number of digits in integer numbers.
         positive:positive, $              ; Flag to indicate positive number values.
         textID:textID, $                  ; The widget identifier of the Text Widget.
         classic:classic }                 ; returns "classic" event structures

    ; Store info structure in Text Widget.

Widget_Control, textID, Set_UValue=info, /No_Copy
RETURN, cw_tlb
END ;----------------------------------------------------------------------------


; The GDL cw_field replacement just calls David Fanning's cw_field (renamed df_cw_field) with proper values,
; and takes advantage of the event_func trapping to convert df_cw_field event to an basic idl event.
; this to avoid maiming the original work of D.Fanning

FUNCTION CW_FIELD, Parent, COLUMN=Column, ROW=Row, $
  EVENT_FUNC = event_func, $
  DOUBLE=doublevalue, ULONG=ulongvalue, $
  FOCUS_EVENTS=focusEventsIn, $
  FLOATING=floatvalue, INTEGER=Integervalue, LONG=Longvalue, STRING=Stringvalue, $
  FONT=LabelFont, FRAME=Frame, TITLE=Title, UVALUE=UValue, VALUE=value, $
  RETURN_EVENTS=Return_Events, ALL_EVENTS=all_events, $
  FIELDFONT=FieldFont, NOEDIT=NoEdit, TEXT_FRAME=Text_Frame, $
  XSIZE=XSize, YSIZE=YSize, UNAME=uname, TAB_MODE=tab_mode, $
  IGNORE_ACCELERATORS=ignoreAccel

   ; Check keyword values.

dataType = 'STRING'
positive=0
IF Keyword_Set(stringvalue) THEN dataType = 'STRING'
IF Keyword_Set(integervalue) THEN dataType = 'INT'
IF Keyword_Set(longvalue) THEN dataType = 'LONG'
IF Keyword_Set(ulongvalue) THEN begin & dataType = 'LONG' & positive=1 & end
IF Keyword_Set(floatvalue) THEN dataType = 'FLOAT'
IF Keyword_Set(doublevalue) THEN dataType = 'DOUBLE'
IF Keyword_set(return_events) THEN cr_Only=1
IF Keyword_set(all_events) THEN cr_Only=0

return,Df_Cw_Field( $           ; The compound widget Df_Cw_Field.
   parent, $                      ; The parent widget. Required for all compound widgets.
   Column=column, $               ; Set this keyword to have Label above Text Widget.
   CR_Only=cr_only, $             ; Set this keyword if you only want Carriage Return events.
   Digits=digits, $               ; Set this keyword to number of allowed digits in INT and LONG values.
   Decimal=decimal, $             ; Set to the number of digits to right of decimal point.
   DoubleValue=doublevalue, $     ; Set this keyword if you want DOUBLE values returned.
   Event_Func=event_func, $       ; Set this keyword to the name of an Event Function.
;   Event_Pro=event_pro, $         ; Set this keyword to the name of an Event Procedure.
   FieldFont=fieldfont, $         ; The font name for the text in the Text Widget.
   FloatValue=floatvalue, $       ; Set this keyword for FLOAT values.
   Frame=frame, $                 ; Set this keyword to put a frame around the compound widget.
   IntegerValue=integervalue, $   ; Set this keyword for INTEGER values.
   LabelFont=labelfont, $         ; The fon name for the text in the Label Widget.
   LabelSize=labelsize, $         ; The X screen size of the Label Widget.
   LongValue=longvalue, $         ; Set this keyword for LONG values.
   Positive=positive, $           ; Set this keyword to only allow positive number values.
   Row=row, $                     ; Set this keyword to have the Label beside the Text Widget. (The default.)
   Scr_XSize=scr_xsize, $         ; The X screen size of the compound widget.
   Scr_YSize=scr_ysize, $         ; The Y screen size of the compound widget.
   StringValue=stringvalue, $     ; Set this keyword for STRING values. (The default.)
   Title=title, $                 ; The text to go on the Label Widget.
   UValue=uvalue, $               ; A user value for any purpose.
   Value=value, $                 ; The "value" of the compound widget.
   XSize=xsize,$                    ; The X size of the Text Widget.
   YSize=ysize,$                    ; The X size of the Text Widget.
   textID=textID, $
   floating=floating,$
   noedit=noedit,$     
   classic=1)
end

PRO Df_Cw_Field_Example_Event, event

; An example event handler for Df_Cw_Field.

Widget_Control, event.top, Get_UValue=info
Widget_Control, event.id, Get_UValue=thisEvent
CASE thisEvent OF
   'Field 1 Event': BEGIN
      Print, ''
      Print, 'Field 1 Event Value: ', *event.value
      END
   'Field 2 Event': BEGIN
      Print, ''
      IF N_Elements(*event.value) EQ 0 THEN Print,'Field 2 is Undefined' ELSE $
         Print, 'Field 2 Event Value: ', *event.value
      END
   'Print It': BEGIN
      Widget_Control, info.field3, Get_Value=theValue
      Print, ''
      Print, 'Field 3 Value: ', theValue
      END
   'PrintFloat': BEGIN
      Widget_Control, info.field2, Get_Value=theValue
      IF N_Elements(*theValue) EQ 0 THEN Print, 'Field 2 Value is Undefined' ELSE $
         Print, 'Field 2 Value: ', *theValue
      END
   'Set It': BEGIN
      Widget_Control, info.field3, Set_Value='Coyote Rules!'
      END
   'Quit': Widget_Control, event.top, /Destroy
ENDCASE
END ;----------------------------------------------------------------------------


; An example program to exercise some of the features of DF_CW_FIELD.
; All events are returned for Field 1, which only allows INTEGER values.
; Only Carriage Return events are returned for Field 2, which allows FLOAT values.
; Field 3 allows you to obtain and set values through the normal WIDGET_CONTROL mechanism.

tlb = Widget_Base(Column=1)
field1 = Df_Cw_Field(tlb, Title='Integer:', LabelSize=50, digits=4, $
   Value=5, /IntegerValue, Event_Pro='Df_Cw_Field_Example_Event', UValue='Field 1 Event')
field2 = Df_Cw_Field(tlb, Title='Float:', LabelSize=50, Value=45.6, $
   /FloatValue, Event_Pro='Df_Cw_Field_Example_Event', /CR_Only, UValue='Field 2 Event', Decimal=3, /Positive)
field3 = Df_Cw_Field(tlb, Title='String:', LabelSize=50, Value='Coyote Rules!')
button = Widget_Button(tlb, Value='Print Value of String', UValue="Print It")
button = Widget_Button(tlb, Value='Set Value of String', UValue='Set It')
button = Widget_Button(tlb, Value='Print Floating Value', UValue='PrintFloat')
button = Widget_Button(tlb, Value='Quit', UValue='Quit')
Widget_Control, tlb, /Realize, Set_UValue={field1:field1, field2:field2, field3:field3}
XManager, 'Df_Cw_Field_Example', tlb, /No_Block
END
