;+ ; NAME: ; FSC_FIELD ; ; PURPOSE: ; ; The purpose of this compound widget is to provide an alternative ; to the CW_FIELD widget offered in the IDL distribution. One weakness ; of the CW_FIELD compound widget 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 ; will be helpful. For example, you can now assign an event handler ; to the compound widget, ask for positive numbers only, and limit ; the number of digits in a number, or the number of digits to the ; right of a decimal point. The program is written as a widget object, ; which allows the user to call object methods directly, affording ; even more flexibility in use. This program replaces the earlier ; programs FSC_INPUTFIELD and COYOTE_FIELD. ; ; The program consists of a label widget next to a one-line text widget. ; The "value" of the compound widget is shown in the text widget. If the ; value is a number, it will not be possible (generally) to type ; alphanumeric values in the text widget. String values behave like ; strings in any one-line text widget. ; ; AUTHOR: ; ; FANNING SOFTWARE CONSULTING ; David Fanning, Ph.D. ; 1645 Sheely Drive ; Fort Collins, CO 80526 USA ; Phone: 970-221-0438 ; E-mail: david@idlcoyote.com ; Coyote's Guide to IDL Programming: http://www.idlcoyote.com/ ; ; CATEGORY: ; ; General programming. ; ; TYPICAL CALLING SEQUENCE: ; ; fieldID = FSC_FIELD(parent, Title="X Size:", Value=256, Object=fieldObject, Digits=3) ; ; 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. ; The default is to have the Label widget in a row with the Text widget. ; ; CR_ONLY Set this keyword if you only want Carriage Return events returned to ; your event handler. If this keyword is not set, all events are returned. ; Setting this keyword has no effect unless either the EVENT_PRO or ; EVENT_FUNC keyword is used. ; ; DECIMAL Set this keyword to the number of digits to the right of the decimal ; point in floating point or double precision numbers. Ignored for STRING values. ; ; DIGITS Set this keyword to the number of digits permitted in integer numbers. ; ; EVENT_FUNC Set this keyword to the name of an event handler 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 handler 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. ; ; FRAME Set this keyword to put a frame around the compound widget. ; ; FOCUS_EVENTS Set this keyword to enable event generation for keyboard focus ; events. Ignored unless EVENT_FUNC or EVENT_PRO keywords are specified. ; ; HIGHLIGHT Set this keyword to highlight the existing text if the widget gain ; the keyboard focus. This keyword MUST be set for tabbing to work naturally ; in IDL 6.2 and higher. ; ; LABEL_LEFT Set this keyword to align the text on the label to the left. ; ; LABEL_RIGHT Set this keyword to align the text on the label to the right. ; ; LABELFONT The font name for the text in the label widget. ; ; LABELSIZE The X screen size of the label widget. ; ; NAME A string containing the name of the object. The default is ''. ; ; NOEDIT Set this keyword to allow no user editing of the input text widget. ; ; NONSENSITIVE Set this keyword to make the input text widget non-sensitive. ; ; POSITIVE Set this keyword if you want only positive numbers allowed. ; ; SCR_XSIZE The X screen size of the compound widget. ; ; SCR_YSIZE The Y screen size of the compound widget. ; ; TITLE The string text placed on the label widget. ; ; UNDEFINED Set this keyword to the value to use for "undefined" values. If ; not set, then !Value.F_NAN is used for numerical fields and a ; NULL string is used for string fields. This applies to values ; obtained with the GET_VALUE method or the GET_VALUE function. ; ; UVALUE A user value for any purpose. ; ; VALUE The "value" of the compound widget. Any type of integer, floating, or string ; variable is allowed. The data "type" is determined automatically from the ; value supplied with this keyword. Be sure you set the type appropriately for ; your intended use of the value. ; ; XSIZE The X size of the text widget in the usual character units. ; ; OUTPUT KEYWORDS: ; ; OBJECT Set this keyword to a named variable to receive the compound widget's ; object reference. This is required if you wish to call methods on the object. ; Note that the object reference is also available in the event structure ; generated by the widget object. Note that the object reference will be ; necessary if you want to get or set values in the compound widget. ; ; COMMON BLOCKS: ; ; None. ; ; RESTRICTIONS: ; ; Requires cgDblToStr from the Coyote Library: ; http://www.idlcoyote.com/programs/cgdbltostr.pro ; ; 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 = { FSC_FIELD_EVENT, $ ; 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. ; OBJECT: Obj_New(), $ ; The "self" object reference. Provided so you can call methods. ; VALUE: Ptr_New(), $ ; A pointer to the widget value. ; TYPE:"" ; A string indicating the type of data in the VALUE field. ; } ; ; Note that if the field is "empty", the VALUE will be a pointer ; to an undefined variable. You should check this value before you ; use it. You code will look something like this: ; ; IF N_Elements(*event.value) EQ 0 THEN $ ; Print, 'Current Value UNDEFINED.' ELSE $ ; Print, 'Current Value: ', *event.value ; ; GETTING and SETTING VALUES: ; ; Almost all the properties of the widget can be obtained or set via ; the object's GetProperty and SetProperty methods (described below). ; Traditional compound widgets have the ability to get and set the "value" ; of the compound widget identifier (e.g., fieldID in the calling ; sequence above). Unfortunately, it is impossible to retreive a variable ; in this way when the variable is undefined. In practical terms, this ; means that the undefined variable must be set to *something*. You can ; determine what that something is with the UNDEFINED keyword, or I will set ; it to !VALUES.F_NAN for numerical fields and to the null string for string ; fields. In any case, you will have to check for undefined variables before ; you try to do something with the value. For a numerical field, the code ; might look something like this: ; ; fieldID = FSC_FIELD(parent, Title="X Size:", Value=256, Object=fieldObject, Digits=3) ; currentValue = fieldObject->Get_Value() ; IF Finite(currentValue) EQ 0 THEN Print, 'Value is Undefined' ELSE Print, currentValue ; ; Additional examples are provided in the numerical example fields in Example Program below. ; ; Setting the value of the compound widget is the same as calling the Set_Value ; method on the object reference. In other words, these two statements are equivalent. ; ; fieldObject->Set_Value, 45.4 ; Widget_Control, fieldID, Set_Value=45.4 ; ; The data type of the value is determined from the value itself. Be sure you set it appropriately. ; ; OBJECT PROCEDURE METHODS: ; ; GetProperty -- This method allows various properties of the widget to be ; returned via output keywords. The keywords that are available are: ; ; CR_Only -- A flag, if set, means only report carriage return events. ; DataType -- The data type of the field variable. ; 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 -- The name of the event handler function. ; Event_Pro -- The name of the event handler function. ; Has_Focus -- Set to 1 if the text widget currently has the keyboard focus. ; Highlight -- The highlight flag. ; NoEdit -- The NoEdit flag. ; NonSensitive -- The NonSensitive flag. ; Undefined -- The "value" of any undefined value. ; UValue -- The user value assigned to the compound widget. ; Value -- The "value" of the compound widget. ; Name -- A scalar string name of the object. ; ; Resize -- This method allows you to resize the compound widget's text field. ; The value parameter is an X screen size for the entire widget. The text ; widget is sized by using the value obtained from this value minus the ; X screen size of the label widget. ; ; objectRef->Resize, screen_xsize_value ; ; Set_Value -- This method allows you to set the "value" of the field. It takes ; one positional parameter, which is the value. ; ; objectRef->Set_Value, 5 ; ; SetProperty -- This method allows various properties of the widget to be ; set via input keywords. The keywords that are available are: ; ; CR_Only -- Set this keyword if you only want Carriage Return events. ; Decimal -- Set this keyword to the number of digits to the right of the decimal ; point in FLOAT and DOUBLE numbers. ; Digits -- Set this keyword to the number of digits permitted in INTERGER and LONG numbers. ; Event_Func -- Set this keyword to the name of an Event Function. ; Event_Pro -- Set this keyword to the name of an Event Procedure. ; Highlight -- Set this keyword to highlight the existing text ; when the widget gets the keyboard focus ; LabelSize -- The X screen size of the Label Widget. ; Name -- A scalar string name of the object. (default = '') ; NoEdit -- Set this keyword to make the text widget uneditable ; NonSensitive -- Set this keyword to make the widget nonsensitive ; Scr_XSize -- The X screen size of the text widget. ; Scr_YSize -- The Y screen size of the text widget. ; 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. ; ; SetTabNext -- This method allows you to specify which field to go to when a TAB character ; is typed in the text widget. See the Example program below for an example of how to ; use this method. ; ; OBJECT FUNCTIONS METHODS: ; ; Get_Value -- Returns the "value" of the field. No parameters. Will be undefined ; if a "number" field is blank. Should be checked before using: ; ; IF N_Elements(objectRef->Get_Value()) NE 0 THEN Print, Value is: ', objectRef->Get_Value() ; ; GetID -- Returns the widget identifier of the compound widget's top-level base. ; (The first child of the parent widget.) No parameters. ; ; GetLabelSize -- Returns the X screen size of the label widget. No parameters. ; ; GetTextID -- Returns the widget identifier of the compound widget's text widget. ; No parameters. ; ; GetTextSize -- Returns the X screen size of the text widget. No parameters. ; ; PRIVATE OBJECT METHODS: ; ; Although there is really no such thing as a "private" method in IDL's ; object implementation, some methods are used internally and not meant to ; be acessed publicly. Here are a few of those methods. I list them because ; it may be these private methods are ones you wish to override in subclassed ; objects. ; ; MoveTab -- This method moves the focus to the widget identified in the "next" field, ; which must be set with the SetTabNext method. No parameters. Called automatically ; when a TAB character is typed in the text widget. ; ; Text_Events -- The main event handler method for the compound widget. All ; text widget events are processed here. ; ; ReturnValue -- This function method accepts a string input value and converts ; it to the type of data requested by the user. ; ; Validate -- This function method examines all text input and removes unwanted ; characters, depending upon the requested data type for the field. It makes it ; impossible, for example, to type alphanumeric characters in an INTEGER field. ; ; EXAMPLE: ; ; An example program is provided at the end of the FSC_FIELD code. To run it, ; type these commands: ; ; IDL> .Compile FSC_Field ; IDL> Example ; ; MODIFICATION HISTORY: ; ; Written by: David W. Fanning, 18 October 2000. Based heavily on an earlier ; FSC_INPUTFIELD program and new ideas about the best way to write ; widget objects. ; Added LABEL_LEFT, LABEL_RIGHT, and UNDEFINED keywords. 29 Dec 2000. DWF. ; Modified the way the value is returned in the GET_VALUE method and the ; GET_VALUE function. Modified Example program to demonstrate. 30 Dec 2000. DWF. ; Added NOEDIT and NONSENSITIVE keywords, with corresponding SETEDIT and SETSENNSITIVE ; methods. 19 Jan 2001. DWF. ; Actually followed through with the changes I _said_" I made 29 Dec 2000. (Don't ask....) 13 June 2001. DWF. ; Added GetTextSize and GetLabelSize methods for obtaining the X screen ; size of the text and label widgets, respectively. 21 July 2001. DWF. ; Fixed a problem in SetProperty method where I was setting self.xsize, which doesn't exist. 24 April 2002. DWF. ; Small modification to the SetEdit method. 6 August 2003. DWF. ; Added Highlight keyword. Ported Focus_Events keyword from ; fsc_inputfield.pro. Updated documentation. 17 November ; 2004. DWF and Benjamin Hornberger ; Added Has_Focus keyword to the GetProperty method. 18 November ; 2004. Benjamin Hornberger ; Fixed bug in GetProperty method (set value to *self.undefined if ; *self.value is undefined. 24 Feb 2004. Benjamin Hornberger ; Modified FOCUS_EVENTS keyword handling so that *all* focus events are now ; passed to specified event handlers. Check event.select to see if the ; widget is gaining or losing focus. 10 August 2005. DWF. ; Added new tabbing functionality, introduced in IDL 6.2. To use tabbing ; functionality natually, the HIGHTLIGHT keywords must be set. ; See included EXAMPLE program for details. 10 August 2005. DWF. ; Added functionality to covert double precision values to strings properly. 30 Nov 2005. DWF. ; Set the default fonts to be the current widget font, rather than the default widget font. 4 Oct 2008. DWF. ; Fixed a problem with validating a float or double value when it was written with ; exponential notation. 2 April 2010. DWF. ;- ;******************************************************************************************; ; Copyright (c) 2008, by Fanning Software Consulting, Inc. ; ; All rights reserved. ; ; ; ; Redistribution and use in source and binary forms, with or without ; ; modification, are permitted provided that the following conditions are met: ; ; ; ; * Redistributions of source code must retain the above copyright ; ; notice, this list of conditions and the following disclaimer. ; ; * Redistributions in binary form must reproduce the above copyright ; ; notice, this list of conditions and the following disclaimer in the ; ; documentation and/or other materials provided with the distribution. ; ; * Neither the name of Fanning Software Consulting, Inc. nor the names of its ; ; contributors may be used to endorse or promote products derived from this ; ; software without specific prior written permission. ; ; ; ; THIS SOFTWARE IS PROVIDED BY FANNING SOFTWARE CONSULTING, INC. ''AS IS'' AND ANY ; ; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ; ; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT ; ; SHALL FANNING SOFTWARE CONSULTING, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, ; ; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED ; ; TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; ; ; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ; ; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ; ; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ; ; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ; ;******************************************************************************************; FUNCTION FSC_Field_WidgetFont, DEFAULT=default ; Build a small widget to determine the current ; and default widget fonts. base = Widget_Base(MAP=0) button = Widget_Button(base, Value='TEST') ; Checking before realization gives default font. defaultFont = Widget_Info(button, /FONTNAME) ; Checking after realization gives current font. Widget_Control, base, /REALIZE currentFont = Widget_Info(button, /FONTNAME) ; Clean up. Widget_Control, base, /DESTROY IF Keyword_Set(default) THEN $ RETURN, defaultFont ELSE $ RETURN, currentFont END ;----------------------------------------------------------------------------------------------------------------------------- FUNCTION FSC_Field::GetLabelSize ; This method returns the X screen size of the label widget. geom = Widget_Info(self.labelID, /Geometry) RETURN, geom.scr_xsize END ;----------------------------------------------------------------------------------------------------------------------------- FUNCTION FSC_Field::GetTextSize ; This method returns the X screen size of the text widget. geom = Widget_Info(self.textID, /Geometry) RETURN, geom.scr_xsize END ;----------------------------------------------------------------------------------------------------------------------------- PRO FSC_Field::MoveTab ; If the TABNEXT field has a valid widget, will set the ; focus to this widget when a TAB event occurs in the widget. IF NOT Widget_Info(self.tabnext, /Valid_ID) THEN RETURN Widget_Control, self.tabnext, /Input_Focus Widget_Control, self.tabnext, Get_Value=theText theText = theText[0] Widget_Control, self.tabnext, Set_Text_Select=[0,StrLen(theText)] END ;----------------------------------------------------------------------------------------------------------------------------- PRO FSC_Field::SetTabNext, nextID ; This method assigns a text id the TABNEXT field of ; the object. This is required for the MOVETAB method. self.tabnext = nextID END ;----------------------------------------------------------------------------------------------------------------------------- FUNCTION FSC_Field::GetTextID ; This method returns the ID of the text widget of the compound widget. RETURN, self.textID END ;----------------------------------------------------------------------------------------------------------------------------- PRO FSC_Field::Resize, newsize ; This method resizes the widget by making the text widget fit the new size. l = Widget_Info(self.labelID, /Geometry) Widget_Control, self.textID, Scr_XSize = (newsize - l.scr_xsize) ; Set the text widget sensitivitiy. Widget_Control, self.textID, Sensitive=1-Keyword_Set(self.nonsensitive) END ;----------------------------------------------------------------------------------------------------------------------------- FUNCTION FSC_Field::GetID ; This method returns the ID of the top-level base of the compound widget. RETURN, self.tlb END ;----------------------------------------------------------------------------------------------------------------------------- FUNCTION FSC_Field::Geometry ; This method returns the geometry of the compound widget. RETURN, Widget_Info(self.tlb,/Geometry) END ;----------------------------------------------------------------------------------------------------------------------------- FUNCTION FSC_Field::Get_Value ; This method returns the actual value of the compound widget. IF N_Elements(*self.theValue) EQ 0 THEN RETURN, *self.undefined ELSE RETURN, *self.theValue END ;----------------------------------------------------------------------------------------------------------------------------- PRO FSC_Field::Set_Value, value ; This method sets the value of the compound widget. ; Error Handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /Cancel ok = cgErrorMsg(/Traceback) RETURN ENDIF dataType = Size(value, /TNAME) CASE dataType OF 'BYTE' : BEGIN genType = 'INTEGER' dataType = 'INT' positive = 1 value = Fix(value) Message, 'BYTE data not supported. Value will be converted to INT.', /Informational END 'INT' : genType = 'INTEGER' 'LONG' : genType = 'INTEGER' 'LONG64' : genType = 'INTEGER' 'UINT' : genType = 'UNSIGNED' 'ULONG' : genType = 'UNSIGNED' 'ULONG64': genType = 'UNSIGNED' 'FLOAT' : genType = 'FLOAT' 'DOUBLE' : genType = 'DOUBLE' 'STRING' : genType = 'STRING' ELSE : Message, 'Data type ' + dataType + ' is not supported. Returning.' ENDCASE self.dataType = dataType self.gentype = genType IF self.gentype EQ 'DOUBLE' THEN theText = cgDblToStr(value) ELSE theText = StrTrim(value,2) theText = self->Validate(theText) ; Load the value in the widget. Widget_Control, self.textID, Set_Value=theText, Set_Text_Select=[StrLen(theText),0] self.theText = theText ; Set the actual value of the compound widget. *self.theValue = self->ReturnValue(theText) END ;----------------------------------------------------------------------------------------------------------------------------- FUNCTION FSC_Field::Validate, value ; 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. This is a "private" method. ; ; + 43B ; - 45B ; . 46B ; 0 - 9 48B -57B ; 'eEdD' [101B, 69B, 100B, 68B] ; Error Handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /Cancel ok = cgErrorMsg(/Traceback) testValue = self->ReturnValue(value) IF String(testValue) NE 'NULLVALUE' THEN numCheck = Finite(testValue) ELSE numCheck = 1 IF numCheck THEN BEGIN RETURN, retValue ENDIF ELSE BEGIN ok = Dialog_Message('The requested number is not representable.') RETURN, "" ENDELSE ENDIF ; 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 trailing blank characters to evaluate. value = StrTrim(value, 2) ; A string value should be returned at once. Nothing to check. IF StrUpCase(self.gentype) EQ 'STRING' THEN RETURN, String(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 self.gentype EQ 'INTEGER' THEN BEGIN returnValue = Ptr_New(/Allocate_Heap) asBytes = Byte(value) IF self.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 self.digits GT 0 THEN BEGIN retValue = StrTrim(retValue, 2) IF StrMid(retValue, 0, 1) EQ "-" THEN digits = self.digits + 1 ELSE digits = self.digits retValue = StrMid(retValue, 0, digits) ENDIF RETURN, retValue ENDIF ; Check unsigned data types. IF self.gentype EQ 'UNSIGNED' THEN BEGIN returnValue = Ptr_New(/Allocate_Heap) asBytes = Byte(value) IF self.positive THEN BEGIN IF (asBytes[0] GE 48B AND asBytes[0] LE 57B) THEN *returnValue = [asBytes[0]] ENDIF ELSE BEGIN IF (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 self.digits GT 0 THEN BEGIN retValue = StrTrim(retValue, 2) digits = self.digits retValue = StrMid(retValue, 0, digits) ENDIF RETURN, retValue ENDIF ; Check floating values. (+,-) in first character or after 'eEdD'. ; Only numbers, signs, decimal points, and 'eEdD' allowed. IF (self.gentype EQ 'FLOAT') OR (self.gentype EQ 'DOUBLE') THEN BEGIN returnValue = Ptr_New(/Allocate_Heap) asBytes = Byte(value) IF self.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 self.decimal GE 0 THEN BEGIN theDecimalPt = StrPos(retValue, '.') IF theDecimalPt NE -1 THEN retValue = StrMid(retValue, 0, theDecimalPt + self.decimal + 1) ENDIF ENDIF ELSE retValue = "" Ptr_Free, returnValue ; Is this a representable number? testValue = self->ReturnValue(retValue) IF String(testValue) NE 'NULLVALUE' THEN numCheck = Finite(testValue) ELSE numCheck = 1 IF numCheck THEN BEGIN RETURN, retValue ENDIF ELSE BEGIN ok = Dialog_Message('The requested number is not representable.') RETURN, "" ENDELSE ENDIF END ;----------------------------------------------------------------------------------------------------------------------------- FUNCTION FSC_Field::ReturnValue, inputValue ; This method takes a string and turns it into a number, ; depending upon the current data type of the compound widget. ; This is a "private" method. For numbers, if the input value ; is a null string, then an undefined variable is returned. ; Error Handling. ON_IOERROR, CatchIt CASE self.datatype OF 'BYTE': IF inputValue EQ "" OR inputValue EQ "-" OR inputValue EQ "+" THEN $ retValue = 'NULLVALUE' ELSE retValue = Fix(inputValue) '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) 'LONG64': IF inputValue EQ "" OR inputValue EQ "-" OR inputValue EQ "+" THEN $ retValue = 'NULLVALUE' ELSE retValue = Long64(inputValue) 'UINT': IF inputValue EQ "" OR inputValue EQ "-" OR inputValue EQ "+" THEN $ retValue = 'NULLVALUE' ELSE retValue = UInt(inputValue) 'ULONG': IF inputValue EQ "" OR inputValue EQ "-" OR inputValue EQ "+" THEN $ retValue = 'NULLVALUE' ELSE retValue = ULong(inputValue) 'ULONG64': IF inputValue EQ "" OR inputValue EQ "-" OR inputValue EQ "+" THEN $ retValue = 'NULLVALUE' ELSE retValue = ULong64(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 FSC_Field::TextEvents, event ; The event handler method for the text widget of the compound widget. ; Error Handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /Cancel ok = cgErrorMsg(/Traceback) RETURN, 0 ENDIF ; Get the previous text, the current cursor location in the text widget, ; and indicate this is not a Carriage Return event. previousText = self.theText textLocation = Widget_Info(event.id, /Text_Select) cr_event = 0 ; Handle keyboard focus events. IF Tag_Names(event, /structure_name) EQ 'WIDGET_KBRD_FOCUS' THEN BEGIN IF event.enter THEN BEGIN self.has_focus = 1L IF self.highlight THEN BEGIN Widget_Control, self.textID, get_value=text text = text[0] Widget_Control, self.textID, set_text_select=[0, Strlen(text)] ENDIF IF self.focus THEN BEGIN ; Get the current contents of text widget. Validate it. Widget_Control, self.textID, Get_Value=newText newText = newText[0] validText = self->Validate(newText) ; Load the valid text. self.theText = validText testValue = self->ReturnValue(validText) IF String(testValue) EQ "NULLVALUE" THEN BEGIN Ptr_Free, self.theValue self.theValue = Ptr_New(/Allocate_Heap) ENDIF ELSE *self.theValue = testValue ; Send the keyboard focus events if there is an event handler to accept them. IF self.event_func NE "" THEN BEGIN thisEvent = {FSC_Field_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self} RETURN, thisEvent ENDIF IF self.event_pro NE "" THEN BEGIN thisEvent = {FSC_Field_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self} RETURN, thisEvent ENDIF ENDIF RETURN, 0 ENDIF ELSE BEGIN self.has_focus = 0L Widget_Control, self.textID, set_text_select=[0] IF self.focus THEN BEGIN ; Get the current contents of text widget. Validate it. Widget_Control, self.textID, Get_Value=newText newText = newText[0] validText = self->Validate(newText) ; Load the valid text. self.theText = validText testValue = self->ReturnValue(validText) IF String(testValue) EQ "NULLVALUE" THEN BEGIN Ptr_Free, self.theValue self.theValue = Ptr_New(/Allocate_Heap) ENDIF ELSE *self.theValue = testValue ; Send the keyboard focus events if there is an event handler to accept them. IF self.event_func NE "" THEN BEGIN thisEvent = {FSC_Field_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self} RETURN, thisEvent ENDIF IF self.event_pro NE "" THEN BEGIN thisEvent = {FSC_Field_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self} RETURN, thisEvent ENDIF ENDIF ENDELSE RETURN, 0 ENDIF ; 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 ; If the character is a TAB see if there is something to do. IF event.ch EQ 9B THEN BEGIN self->MoveTab RETURN, 0 ENDIF ; Get the current contents of text widget. Validate it. Widget_Control, self.textID, Get_Value=newText newText = newText[0] validText = self->Validate(newText) ; If it is valid, leave it alone. If not, go back to previous text. IF validText NE newText THEN BEGIN Widget_Control, self.textID, Set_Value=previousText, Set_Text_Select=[textLocation[0]-1,0] ENDIF ELSE BEGIN self.theText = validText testValue = self->ReturnValue(validText) IF String(testValue) EQ "NULLVALUE" THEN BEGIN Ptr_Free, self.theValue self.theValue = Ptr_New(/Allocate_Heap) ENDIF ELSE *self.theValue = testValue ENDELSE ; Is this a Carriage Return event? IF event.ch EQ 10B then cr_event = 1 ENDCASE 'INSERT MULTIPLE CHARACTERS': BEGIN ; Same as above, but for all the characters you are inserting. Widget_Control, self.textID, Get_Value=newText newText = newText[0] validText = self->Validate(newText) IF validText NE newText THEN BEGIN Widget_Control, self.textID, Set_Value=previousText, Set_Text_Select=[textLocation[0]-1,0] ENDIF ELSE BEGIN self.theText = validText testValue = self->ReturnValue(validText) IF String(testValue) EQ "NULLVALUE" THEN BEGIN Ptr_Free, self.theValue self.theValue = Ptr_New(/Allocate_Heap) ENDIF ELSE *self.theValue = testValue ENDELSE ENDCASE 'DELETE TEXT': BEGIN ; Get the current contents of text widget. Validate it. Widget_Control, self.textID, Get_Value=newText newText = newText[0] validText = self->Validate(newText) ; Load the valid text. Widget_Control, self.textID, Set_Value=validText, Set_Text_Select=[textLocation[0],0] self.theText = validText testValue = self->ReturnValue(validText) IF String(testValue) EQ "NULLVALUE" THEN BEGIN Ptr_Free, self.theValue self.theValue = Ptr_New(/Allocate_Heap) ENDIF ELSE *self.theValue = testValue ENDCASE 'SELECT TEXT': ; Nothing to do. ENDCASE ; Do you report all events, or only Carriage Return events? IF self.cr_only THEN BEGIN IF self.event_func NE "" THEN BEGIN thisEvent = {FSC_Field_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self} IF cr_event THEN RETURN, thisEvent ENDIF IF self.event_pro NE "" THEN BEGIN thisEvent = {FSC_Field_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self} IF cr_event THEN RETURN, thisEvent ENDIF ENDIF ELSE BEGIN IF self.event_func NE "" THEN BEGIN thisEvent = {FSC_Field_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self} RETURN, thisEvent ENDIF IF self.event_pro NE "" THEN BEGIN thisEvent = {FSC_Field_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self} RETURN, thisEvent ENDIF ENDELSE RETURN, 0 END ;----------------------------------------------------------------------------------------------------------------------------- PRO FSC_Field::GetProperty, $ ; ; This method allows you to obtain various properties of the compound widget via output keywords. ; CR_Only=cr_only, $ ; Set this keyword if you only want Carriage Return events. DataType=datatype, $ ; The datatype of the compound widget. Decimal=decimal, $ ; The number of digits to the right of the decimal point in FLOAT numbers. Digits=digits, $ ; The number of digits permitted in INTERGERVALUE and LONGVALUE numbers. 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. Focus_Events=focus_events, $ ; Set this keyword to inquire whether or not events are reported when the keyboard focus is lost. Has_Focus=has_focus, $ ; Set this keyword to inquire whether of not the widget has the keyboard focus currently. Highlight=highlight, $ ; The highlight flag. Name=Name, $ ; The name of the object. NoEdit=noedit, $ ; Setting this keywords makes the text widget non-editable. NonSensitive=nonsensitive, $ ; Setting this keywords makes the text widget non-sensitive. Undefined=undefined, $ ; The "value" of any undefined value. UValue=uvalue, $ ; A user value for any purpose. Value=value ; The "value" of the compound widget. ; Error Handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /Cancel ok = cgErrorMsg(/Traceback) RETURN ENDIF ; Get the properties. cr_only = self.cr_only datatype = self.datatype decimal = self.decimal digits = self.digits event_func = self.event_func event_pro = self.event_pro focus_events = self.focus has_focus = self.has_focus highlight = self.highlight name = self.name noedit = self.noedit nonsensitive = self.nonsensitive Widget_Control, self.tlb, Get_UValue=uvalue IF N_Elements(*self.theValue) EQ 0 THEN value = *self.undefined ELSE value = *self.thevalue undefined = *self.undefined END ;-------------------------------------------------------------------------------------------------------------- PRO FSC_Field::SetProperty, $ ; ; This method allows you to set various properties of the compound widget. ; CR_Only=cr_only, $ ; Set this keyword if you only want Carriage Return events. Decimal=decimal, $ ; Set this keyword to the number of digits to the right of the decimal point in FLOAT values.. Digits=digits, $ ; Set this keyword to the number of digits permitted in INTEGER values. 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. Focus_Events=focus_events, $ ; Set this keyword to turn on or off event reporting when the keyboard focus is lost. Highlight=highlight, $ ; Set this keyword to highlight the text when it gets keyboard focus. LabelSize=labelsize, $ ; The X screen size of the Label Widget. Name=name, $ ; A scalar string name for the object. NoEdit=noedit, $ ; Setting this keywords makes the text widget non-editable. NonSensitive=nonsensitive, $ ; Setting this keywords makes the text widget non-sensitive. Scr_XSize=scr_xsize, $ ; The X screen size of the text widget. Scr_YSize=scr_ysize, $ ; The Y screen size of the text widget. Title=title, $ ; The text to go on the Label Widget. Undefined=undefinded, $ ; Set to "value" of undefined value. 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. ; Error Handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /Cancel ok = cgErrorMsg(/Traceback) RETURN ENDIF ; Set the properties, if needed. IF N_Elements(cr_only) NE 0 THEN self.cr_only = cr_only IF Keyword_Set(decimal)THEN self.decimal = decimal IF Keyword_Set(digits)THEN self.digits = digits IF N_Elements(event_func) NE 0 THEN self.event_func = event_func IF N_Elements(event_pro) NE 0 THEN self.event_pro = event_pro IF N_Elements(focus_events) NE 0 THEN self.focus = keyword_set(focus) IF N_Elements(highlight) NE 0 THEN self.highlight = Keyword_Set(highlight) IF N_Elements(labelsize) NE 0 THEN BEGIN Widget_Control, self.labelID, XSize=labelsize ENDIF IF N_Elements(scr_xsize) NE 0 THEN BEGIN Widget_Control, self.textID, Scr_XSize=scr_xsize ENDIF IF N_Elements(scr_ysize) NE 0 THEN BEGIN Widget_Control, self.textID, Scr_YSize=scr_ysize ENDIF IF N_Elements(title) NE 0 THEN Widget_Control, self.labelID, Set_Value=title IF N_Elements(uvalue) NE 0 THEN Widget_Control, self.tlb, Set_UValue=uvalue If N_Elements(name) NE 0 Then self.name = String(name[0]) IF N_Elements(xsize) NE 0 THEN BEGIN Widget_Control, self.textID, XSize=xsize ENDIF IF N_Elements(noedit) THEN BEGIN self.noedit = Keyword_Set(noedit) Widget_Control, self.textID, Editable=1-self.noedit ENDIF IF N_Elements(nonsensitive) THEN BEGIN self.nonsensitive = Keyword_Set(nonsensitive) Widget_Control, self.textID, Sensitive=1-self.nonsensitive ENDIF ; Set up the value. IF N_Elements(value) NE 0 THEN BEGIN ; Set up data type and general type. dataType = Size(value, /TNAME) CASE dataType OF 'BYTE' : BEGIN genType = 'INTEGER' dataType = 'INT' positive = 1 *self.theValue = Fix(*self.theValue) Message, 'BYTE data not supported. Value will be converted to INT.', /Informational END 'INT' : genType = 'INTEGER' 'LONG' : genType = 'INTEGER' 'LONG64' : genType = 'INTEGER' 'UINT' : genType = 'UNSIGNED' 'ULONG' : genType = 'UNSIGNED' 'ULONG64': genType = 'UNSIGNED' 'FLOAT' : genType = 'FLOAT' 'DOUBLE' : genType = 'DOUBLE' 'STRING' : genType = 'STRING' ELSE : BEGIN Ptr_Free, self.theValue self.theValue = Ptr_New(/Allocate_Heap) Message, 'Data type ' + dataType + ' is not supported. Returning.', /NoName END ENDCASE CASE Size(value, /TNAME) OF 'FLOAT': self.theText = StrTrim(String(value, FORMAT='(F0)'),2) 'DOUBLE': self.theText = StrTrim(String(value, FORMAT='(D0)'),2) ELSE: self.theText = StrTrim(value, 2) ENDCASE *self.theValue = self->ReturnValue(self.theText) Widget_Control, self.textID, Set_Value=self.theText self.dataType = datatype self.genType = genType IF N_Elements(undefined) EQ 0 THEN BEGIN IF genType EQ 'STRING' THEN undefined = "" ELSE undefined = !VALUES.F_NAN ENDIF ENDIF END ;-------------------------------------------------------------------------------------------------------------- PRO FSC_Field::SetEdit, editvalue ; Error Handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /Cancel ok = cgErrorMsg(/Traceback) RETURN ENDIF IF N_Elements(editvalue) NE 0 THEN $ Widget_Control, self.textID, Editable=Keyword_Set(editvalue) END ;-------------------------------------------------------------------------------------------------------------- PRO FSC_Field::SetSensitive, value ; Error Handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /Cancel ok = cgErrorMsg(/Traceback) RETURN ENDIF IF N_Elements(value) EQ 0 THEN value = 1 self.nonsensitive = 1-value Widget_Control, self.textID, Sensitive=value END ;-------------------------------------------------------------------------------------------------------------- FUNCTION FSC_Field::INIT, $ ; The compound widget FSC_Field INIT method.. 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. Decimal=decimal, $ ; Set this keyword to the number of digits to the right of the decimal point in FLOAT. Digits=digits, $ ; Set this keyword to the number of digits permitted in INTEGER values. 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. _Extra=extra, $ ; Passes along extra keywords to the text widget. FieldFont=fieldfont, $ ; The font name for the text in the Text Widget. Focus_Events=focus_events, $ ; Set this keyword to enable event reporting when the keyboard focus is lost or gained. Frame=frame, $ ; Set this keyword to put a frame around the compound widget. Highlight=highlight, $ ; If this keyword is set, the text is highlighted. Label_Left=label_left, $ ; Set this keyword to align the label to the left of the label. Label_Right=label_right, $ ; Set this keyword to align the labe to the right of the label. LabelFont=labelfont, $ ; The font name for the text in the Label Widget. LabelSize=labelsize, $ ; The X screen size of the Label Widget. Name=name, $ ; A scalar string name for the object. NoEdit=noedit, $ ; Setting this keywords makes the text widget non-editable. NonSensitive=nonsensitive, $ ; Setting this keywords makes the text widget non-sensitive. Positive=positive, $ ; Set this keyword to indicate only positive numbers allowed in the field. Scr_XSize=scr_xsize, $ ; The X screen size of the text widget. Scr_YSize=scr_ysize, $ ; The Y screen size of the text widget. Title=title, $ ; The text to go on the Label Widget. Undefined=undefined, $ ; Set to the value for "undefined" field values. 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. ; Error Handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /Cancel ok = cgErrorMsg(/Traceback) RETURN, 0 ENDIF ; A parent is required. IF N_Elements(parent) EQ 0 THEN BEGIN Message, 'A PARENT argument is required. Returning...', /NoName ENDIF ; Check keyword values. 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 = FSC_Field_WidgetFont() IF N_Elements(frame) EQ 0 THEN frame = 0 IF N_Elements(labelfont) EQ 0 THEN labelfont = FSC_Field_WidgetFont() IF N_Elements(labelsize) EQ 0 THEN labelsize = 0 IF N_Elements(name) EQ 0 THEN name = "" noedit = Keyword_Set(noedit) nonsensitive = Keyword_Set(nonsensitive) positive = Keyword_Set(positive) 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 = "" IF N_Elements(xsize) EQ 0 THEN xsize = 0 ; What data type are we looking for? dataType = Size(value, /TNAME) CASE dataType OF 'BYTE' : BEGIN genType = 'INTEGER' dataType = 'INT' positive = 1 value = Fix(value) Message, 'BYTE data not supported. Value will be converted to INT.', /Informational END 'INT' : genType = 'INTEGER' 'LONG' : genType = 'INTEGER' 'LONG64' : genType = 'INTEGER' 'UINT' : genType = 'UNSIGNED' 'ULONG' : genType = 'UNSIGNED' 'ULONG64': genType = 'UNSIGNED' 'FLOAT' : genType = 'FLOAT' 'DOUBLE' : genType = 'DOUBLE' 'STRING' : genType = 'STRING' ELSE : Message, 'Data type ' + dataType + ' is not supported. Returning.', /NoName ENDCASE IF N_Elements(undefined) EQ 0 THEN BEGIN IF genType EQ 'STRING' THEN undefined = "" ELSE undefined = !VALUES.F_NAN ENDIF ; Populate the object. self.cr_only = Keyword_Set(cr_only) self.datatype = datatype self.decimal = decimal self.digits = digits self.focus = Keyword_Set(focus_events) self.highlight = Keyword_Set(highlight) self.gentype = genType self.parent = parent self.event_pro = event_pro self.event_func = event_func self.positive = positive self.undefined = Ptr_New(undefined) self.noedit = noedit self.nonsensitive = nonsensitive If N_Elements(name) NE 0 Then self.name = String(name[0]) IF Keyword_Set(column) THEN row = 0 ELSE row = 1 ; Validate the input value. IF self.gentype EQ 'DOUBLE' THEN value = cgDblToStr(value) ELSE value = StrTrim(value,2) value = self->Validate(value) self.theText = value ; Create the widgets. self.tlb = Widget_Base( parent, $ ; The top-level base of the compound widget. Frame=frame, $ Row=row, $ Column=Keyword_Set(column), $ Base_Align_Center=1, $ UName=name, $ UValue=uvalue, $ Event_Pro=event_pro, $ Func_Get_Value='FSC_Field_Get_Compound_Widget_Value', $ Pro_Set_Value='FSC_Field_Set_Compound_Widget_Value', $ Event_Func=event_func ) ; Update for tabbing in IDL 6.2. IF Float(!Version.Release) GT 6.1 THEN BEGIN Widget_Control, self.tlb, TAB_MODE=1 ENDIF self.labelID = Widget_Label( self.tlb, Value=title, $ ; The Label Widget. Scr_XSize=labelsize, UValue=self, Align_Left=Keyword_Set(label_left), Align_Right=Keyword_Set(label_right)) self.textID = Widget_Text( self.tlb, $ ; The Text Widget. Value=value, $ XSize=xsize, $ YSize=1, $ Scr_XSize=scr_xsize, $ Scr_YSize=scr_ysize, $ sensitive=1-self.nonsensitive, $ Font=fieldfont, $ All_Events=1, $ _Extra=extra, $ kbrd_focus_events=1, $ Event_Func='FSC_Field_Event_Handler', $ UValue={Method:"TextEvents", Object:self}, $ Kill_Notify='FSC_Field_Kill_Notify', $ Editable=1-noedit ) self.theValue = Ptr_New(self->ReturnValue(value)) ; If CR_ONLY or FOCUS_EVENTS are turned on and EVENT_PRO and EVENT_FUNC keywords are ; unspecified, issue a warning message to the command log. IF self.cr_only AND (self.event_pro EQ "" AND self.event_func EQ "") THEN $ Message, /Information, 'WARNING: There is no specified event handler for carriage return events for this widget. Events will be swallowed.' IF self.focus AND (self.event_pro EQ "" AND self.event_func EQ "") THEN $ Message, /Information, 'WARNING: There is no specified event handler for keyboard focus events for this widget. Events will be swallowed.' RETURN, 1 END ;-------------------------------------------------------------------------------------------------------------- PRO FSC_Field::CLEANUP ; This method makes sure there are not pointers left on the heap. Ptr_Free, self.theValue Ptr_Free, self.undefined END ;-------------------------------------------------------------------------------------------------------------- FUNCTION FSC_Field_Event_Handler, event ; The main event handler for the compound widget. It reacts ; to "messages" in the UValue of the text widget. ; The message indicates which object method to call. A message ; consists of an object method and the self object reference. Widget_Control, event.ID, Get_UValue=theMessage event = Call_Method(theMessage.method, theMessage.object, event) RETURN, event END ;----------------------------------------------------------------------------------------------------------------------------- PRO FSC_Field_Event__Define ; The FSC_Field Event Structure. Sent only if EVENT_PRO or EVENT_FUNC keywords ; have defined an event handler for the top-level base of the compound widget. event = { FSC_Field_Event, $ ; 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. Object: Obj_New()} ; The "self" object. END ;----------------------------------------------------------------------------------------------------------------------------- PRO FSC_Field_Set_Compound_Widget_Value, tlb, value ; This utilty routine is invoked when the user tries to set ; the value of the compound widget using the base widget ; identifier of the top-level base of the compound widget. ; The self object is located, and the Set_Value method is called ; on the object. firstChildID = Widget_Info(tlb, /Child) Widget_Control, firstChildID, Get_UValue=self self->Set_Value, value END ;-------------------------------------------------------------------------------------------------------------- FUNCTION FSC_Field_Get_Compound_Widget_Value, tlb ; This utilty routine is invoked when the user tries to get ; the value of the compound widget using the base widget ; identifier of the top-level base of the compound widget. ; The self object is located, and the Get_Value method is called ; on the object. firstChildID = Widget_Info(tlb, /Child) Widget_Control, firstChildID, Get_UValue=self IF N_Elements(self->Get_Value()) EQ 0 THEN RETURN, *self.undefined ELSE RETURN, self->Get_Value() END ;-------------------------------------------------------------------------------------------------------------- PRO FSC_Field_Kill_Notify, textID ; This widget call-back procedure makes sure the self object is ; destroyed when the widget is destroyed. Widget_Control, textID, Get_UValue=message Obj_Destroy, message.object END ;-------------------------------------------------------------------------------------------------------------- PRO FSC_Field__Define objectClass = { FSC_FIELD, $ ; The object class name. parent: 0L, $ ; The parent widget ID. tlb: 0L, $ ; The top-level base of the compound widget. labelID: 0L, $ ; The label widget ID. textID: 0L, $ ; The text widget ID. theText: "", $ ; The actual text in the text widget. theValue: Ptr_New(), $ ; The actual "value" of the text in the text widget. :-) event_func: "", $ ; The name of the specified event handler function. event_pro: "", $ ; The name of the specified event handler procedrue cr_only: 0L, $ ; A flag meaning send only carriage return events. focus: 0L, $ ; A flag to indicate that events should be reported if the keyboard focus is lost has_focus: 0L, $ ; A flag to indicate that the widget has the keyboard focus currently. highlight: 0L, $ ; A flag for highlighting. tabnext: 0L, $ ; The identifier of a widget to receive the cursor focus if a TAB character is detected. decimal: 0, $ ; The number of decimals points in FLOAT and DOUBLE numbers. digits: 0, $ ; The number of digits in INT and LONG numbers. positive: 0, $ ; A flag meaning only positive numbers allowed. datatype: "",$ ; The type of data to be returned from the text widget. gentype: "", $ ; The "general" type of data: INTEGER, UNSIGNED, FLOAT, or STRING. undefined: Ptr_New(), $ ; The "undefined" value. Used in Get_Value methods, etc. noedit: 0L, $ ; A flag indicating whether text widget is editable (0) or not (1). nonsensitive: 0L, $ ; A flag indicating whether text widget is sensitive (0) or not (1). name:"" $ ; a scalar string name for the object } END ;-------------------------------------------------------------------------------------------------------------- FUNCTION FSC_FIELD, $ ; The compound widget FSC_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. Decimal=decimal, $ ; Set this keyword to the number of digits to the right of the decimal point in FLOAT numbers. Digits=digits, $ ; Set this keyword to the number of digits permitted in INTEGER numbers. 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. _Extra=extra, $ ; Passes along extra keywords to the text widget. FieldFont=fieldfont, $ ; The font name for the text in the Text Widget. Focus_Events=focus_events, $ ; Set this keyword to enable event reporting when the keyboard focus is lost. Frame=frame, $ ; Set this keyword to put a frame around the compound widget. Highlight=highlight, $ ; If this keyword is set, the text is highlighted. Label_Left=label_left, $ ; Set this keyword to align the label to the left of the label. Label_Right=label_right, $ ; Set this keyword to align the labe to the right of the label. LabelFont=labelfont, $ ; The font name for the text in the Label Widget. LabelSize=labelsize, $ ; The X screen size of the Label Widget. Name=name, $ ; The name of the object. NoEdit=noedit, $ ; Setting this keywords makes the text widget non-editable. NonSensitive=nonsensitive, $ ; Setting this keywords makes the text widget non-sensitive. Object = obj, $ ; An output keyword that contains the object reference. Positive=positive, $ ; Set this keyword to indicate only positive numbers allowed in the field. Scr_XSize=scr_xsize, $ ; The X screen size of the text widget. Scr_YSize=scr_ysize, $ ; The Y screen size of the text widget. Title=title, $ ; The text to go on the Label Widget. Undefined=undefined, $ ; Set to the value for "undefined" field values. 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. obj = Obj_New("FSC_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. Decimal=decimal, $ ; Set this keyword to the number of digits to the right of the decimal point in FLOAT numbers. Digits=digits, $ ; Set this keyword to the number of digits permitted in INTEGER numbers. 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. _Extra=extra, $ ; Passes along extra keywords to the text widget. FieldFont=fieldfont, $ ; The font name for the text in the Text Widget. Focus_events=focus_events, $ ; Set this keyword to enable event reporting when the keyboard focus is lost. Frame=frame, $ ; Set this keyword to put a frame around the compound widget. Highlight=highlight, $ ; If this keyword is set, the text is highlighted. Label_Left=label_left, $ ; Set this keyword to align the label to the left of the label. Label_Right=label_right, $ ; Set this keyword to align the labe to the right of the label. LabelFont=labelfont, $ ; The font name for the text in the Label Widget. LabelSize=labelsize, $ ; The X screen size of the Label Widget. Name=name, $ ; The name of the object. NoEdit=noedit, $ ; Setting this keywords makes the text widget non-editable. NonSensitive=nonsensitive, $ ; Setting this keywords makes the text widget non-sensitive. Positive=positive, $ ; Set this keyword to indicate only positive numbers allowed in the field. Scr_XSize=scr_xsize, $ ; The X screen size of the text widget. Scr_YSize=scr_ysize, $ ; The Y screen size of the text widget. Title=title, $ ; The text to go on the Label Widget. Undefined=undefined, $ ; Set to the value for "undefined" field values. 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. IF Obj_Valid(obj) THEN RETURN, obj->GetID() ELSE RETURN, -1L END ;-------------------------------------------------------------------------------------------------------------- PRO Example_Event, event ; An example event handler for FSC_Field. Widget_Control, event.top, Get_UValue=info Widget_Control, event.id, Get_UValue=thisEvent ; Not interested in losing keyboard focus events. theName = Tag_Names(event, /Structure_Name) IF theName EQ 'WIDGET_KBRD_EVENT' THEN BEGIN IF event.type EQ 0 THEN RETURN ENDIF ; What kind of event is this? CASE thisEvent OF 'Field 1 Event': BEGIN ; Demonstate various ways to test if the value from this field is undefined. Print, '' IF N_Elements(*event.value) EQ 0 THEN Print, 'Field 1 Value is Undefined from Event Structure' ELSE $ Print, 'Field 1 Value from Event Structure: ', *event.value Widget_Control, info.field1id, Get_Value=theValue IF Finite(theValue) EQ 0 THEN Print, 'Field 1 Value is Undefined and Assigned Value: ', theValue ELSE $ Print, 'Field 1 Value via Widget Get_Value Function: ', theValue IF Finite(info.field1->Get_Value()) EQ 0 THEN Print, 'Field 1 Value is Undefined from Object Get_Value Function' ELSE $ Print, 'Field 1 Value via Object Get_Value Function: ', info.field1->Get_Value() END 'Field 2 Event': BEGIN Print, '' IF N_Elements(*event.value) EQ 0 THEN Print, 'Field 2 Value is Undefined' ELSE $ Print, 'Field 2 Value: ', *event.value theValue = info.field2->Get_Value() IF theValue EQ -9999.0 THEN Print, 'Field 2 Value is Undefined and Assigned Value: ', theValue ELSE $ Print, 'Field 2 Value via Object Get_Value Function: ', theValue END 'Print It': BEGIN theValue =info.field3->Get_Value() Print, '' Print, 'Field 3 Value: ', theValue END 'Set It': BEGIN info.field3->Set_Value, 'Coyote Rules!' END 'Quit': Widget_Control, event.top, /Destroy 'ChangeToUInt': BEGIN info.field1->SetProperty, Title='Unsigned:', Value=UINT(RandomU(seed, 1)*100) END 'ChangeToFloat': BEGIN info.field1->SetProperty, Title='Float:', Value=RandomU(seed, 1)*100 END 'ChangeToString': BEGIN info.field1->SetProperty, Title='String:', Value='Coyote Jules' END 'PrintFloat': BEGIN IF N_Elements(info.field2->Get_Value()) EQ 0 THEN Print, 'Field 2 Value is Undefined' ELSE $ Print, 'Field 2 Value: ', info.field2->Get_Value() END 'LabelSize': BEGIN Widget_Control, event.top, Update=0 currentSize = info.labelsize info.field1->SetProperty, LabelSize=info.labelsize info.field2->SetProperty, LabelSize=info.labelsize info.field3->SetProperty, LabelSize=info.labelsize IF currentsize EQ 75 THEN info.labelsize = 50 ELSE info.labelsize = 75 Widget_Control, event.top, Update=1 END 'MakeEditable': BEGIN Widget_Control, event.id, Get_Value=buttonValue CASE buttonValue OF 'Make String Field Editable': BEGIN info.field3->SetEdit, 1 Widget_Control, event.id, Set_Value='Make String Field Non-Editable' END 'Make String Field Non-Editable': BEGIN info.field3->SetEdit, 0 Widget_Control, event.id, Set_Value='Make String Field Editable' END ENDCASE END 'MakeSensitive': BEGIN Widget_Control, event.id, Get_Value=buttonValue CASE buttonValue OF 'Make String Field Sensitive': BEGIN info.field3->SetSensitive, 1 Widget_Control, event.id, Set_Value='Make String Field Non-Sensitive' END 'Make String Field Non-Sensitive': BEGIN info.field3->SetSensitive, 0 Widget_Control, event.id, Set_Value='Make String Field Sensitive' END ENDCASE END ENDCASE IF Widget_Info(event.top, /Valid_ID) THEN Widget_Control, event.top, Set_UValue=info END ;-------------------------------------------------------------------------------------------------------------- PRO Example, field1, field2, field3 ; An example program to exercise some of the features of FSC_FIELD. tlb = Widget_Base(Column=1) button = Widget_Button(tlb, Value='Change First Field to Float', $ UValue='ChangeToFloat') button = Widget_Button(tlb, Value='Change First Field to String', $ UValue='ChangeToString') button = Widget_Button(tlb, Value='Change First Field to Unsigned Integer', $ UValue='ChangeToUInt') ; Create an integer field, no more than 4 digits in the value. field1id = FSC_FIELD(tlb, Title='Integer:', LabelSize=50, Digits=4, Object=field1, $ Value=5, UValue='Field 1 Event', Event_Pro='Example_Event', /CR_Only, /Highlight) ; Create a floating point field. Only two decimal points to the right of the decimal. ; Set the Undefined value to -9999.0. field = FSC_FIELD(tlb, Title='Double:', LabelSize=50, Value=-123456789.1234567891234D, Object=field2, Undefined=-9999.0, $ /CR_Only, UValue='Field 2 Event', Event_Pro='Example_Event', Decimal=8, /Highlight) ; Create a string field. field = FSC_FIELD(tlb, Title='String:', LabelSize=50, Value='Coyote Rules!', Object=field3, /Nonsensitive, /Highlight) ; Set up TABing between fields. ;field1->SetTabNext, field2->GetTextID() ;field2->SetTabNext, field3->GetTextID() ;field3->SetTabNext, field1->GetTextID() 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='Change Size of Labels', UValue='LabelSize') button = Widget_Button(tlb, Value='Print Floating Value', UValue='PrintFloat') button = Widget_Button(tlb, Value='Make String Field Editable', UValue='MakeEditable') button = Widget_Button(tlb, Value='Make String Field Sensitive', UValue='MakeSensitive') button = Widget_Button(tlb, Value='Quit', UValue='Quit') Widget_Control, tlb, /Realize, Set_UValue={field1:field1, field2:field2, field3:field3, field1id:field1id, labelsize:75} XManager, 'example', tlb, /No_Block END