;{ Notes ; Created by Xombie - 4/25/2005 ; ; 4/26/2005 ; 1. Fixed a bug when copying from the xMask gadget. ; 2. Updated to include simple events like 'Return Key', 'Lost Focus', 'Changing' and 'Bad Key' events. ; ; 4/27/2005 ; 1. Fixed a problem when the xMask gadget is created in a container. It wouldn't return a return event. ; 2. Added xmDisableGadget() to set a gadget to readonly or not. ; 3. Reworked xmGetGadgetText(). I really need feedback from other people on how they would like this to work. ; ; Honestly, I created the gadget for an extremely specific purpose in mind. ; I added in all these extra abilities as a kind of afterthought in case other people ; were interested. So give me some feedback on how the gadget should behave. ; ; 1. Need some input on how the gadget should react to certain things. For example, how xmGetGadgetText() should return a value when a field is empty. ; ; 4/28/2005 ; 1. Added an experimental mask - #Financial. It's case sensitive so when you set the mask be sure it's properly capitalized. ;} ;- Structures Structure s_xMask ; idGadget.l ; This is the gadget id used for the PB equivalent of the gadget. handleGadget.l ; This is the handle for the xMask gadget. Returned from CreateWindowEx_() handleParent.l ; This is the handle of the parent control. Usually WindowID() returns this unless the ; control is inside of a container gadget or similar. OldCallback.l ; This is the address of the old callback procedure attached to the xMask gadget. x.l ; Left coordinate of the gadget. y.l ; Top coordinate of the gadget. Width.l ; Width of the gadget. Height.l ; Height of the gadget. Mask.s ; The mask used with the xMask gadget. PlaceHolder.b ; This is the character used as a placeholder. It's the asc() equivalent. DefaultText.s ; This is the default text used for the xMask gadget. This can be empty if ; no default text is wanted. MaxLength.l ; The maximum length of the xMask gadget, including the mask. SelectAllOnFocus.b ; If True, when the the xMask gadget is selected it will select the whole line. FinancialShowSeparator.b ; True if we want to show the thousands separator character for the special case Financial mask. FinancialAutoDecimal.b ; True if we want to allows show a decimal place for the special case Financial mask. FinancialDecimalPlaces.b ; The number of decimal places to use for the special case Financial mask. FinancialNegativeAsParan.b ; True if we want to display a negative value using paranthesis rather than a '-' sign. FinancialRoundTruncatedDecimals.b ; True if, when manually setting number to our special case Financial mask and it contains ; more decimal places than allowed, we want to round the number. AddressChangeEvent.l ; This will be the address of the event that is called as our xMask gadget is changed. AddressBadKeyEvent.l ; This will be the address of the event that is called if we type a bad character in our xMask gadget. AddressLostFocusEvent.l ; This will be the address of the event that is called when our xMask gadget loses focus. AddressReturnEvent.l ; This will be the address of the event that is called when the user presses the 'enter' key in our gadget. EndStructure Structure s_xmTrack ; idPointer.l ; This is the pointer for the xMask structure. idGadget.l ; This is the PB gadget constant. handleGadget.l ; And this is the handle to the xMask gadget (from *xMask\handleGadget). EndStructure ;- Linked List Tracker NewList xmTrack.s_xmTrack() ; We'll use this linked list to keep track of all of our xMask gadgets in use. ;- Private xMask Functions - Financial Specific Procedure.s xm_FinancialValue(*xMask.s_xMask, inText.s) ; This will strip a financial number down to the basic number. Protected sReturn.s ; Protected HoldTextChar.b ; Protected PositionText.l ; If inText = "" : ProcedureReturn "0" : EndIf ; If Left(inText, 1) = "(" And Right(inText, 1) = ")" ; Our number is negative if it's surrounded by parantheses. inText = "-" + Mid(inText, 2, Len(inText) - 2) ; Convert the parantheses to a negative sign. EndIf ; PositionText = 1 ; Our string functions are 1 based. Repeat ; HoldTextChar = Asc(Mid(inText, PositionText, 1)) ; Retrieve the ascii value of the character at the current position. If HoldTextChar = 45 ; Dash character (negative). sReturn = sReturn + "-" ; ElseIf HoldTextChar = 46 ; Decimal character. sReturn = sReturn + "." ; ElseIf HoldTextChar >=48 And HoldTextChar <= 57 ; Numbers sReturn = sReturn + Chr(HoldTextChar) ; EndIf ; PositionText + 1 ; Decrement our position. Until PositionText = Len(inText) + 1 ; ProcedureReturn sReturn ; EndProcedure Procedure.s xm_FinancialMaskToText(inText.s, ShowSeparator.b, ForceDecimal.b, NumberDecimals.b, RoundDecimal.b, NegativeParan.b, EditingMask.b) ; This code will take text and convert it to a financial figure (ex., 1000.2626 = $1,000.26) ; based on the options passed. ShowSeparator will force the thousands separator to be shown ; if it is True. ForceDecimal will automatically display a decimal value regardless of what ; the original number includes. NumberDecimals is the desired number of decimal places. ; RoundDecimal will round the incoming number's decimal places to fit the NumberDecimals ; parameter if True, otherwise it will just truncate the number of decimals. EditingMask ; controls whether we are just showing the plain value or not. If True, we will not show ; any parantheses for negative values, will not show commas and will not show the $ sign. Protected PositionText.l ; This will store the current position in the text. Protected sReturn.s ; This will be the combined string we return. Protected HoldTextChar.b ; Protected CaughtInteger.b ; True if we've caught a non-decimal integer. Protected ShowNegative.b ; False (0) if we do not want to show the negative sign. Protected TextContainsDecimal.b ; False (0) if the incoming text does not contain a decimal place. Otherwise, returns the position of the decimal. Protected CaughtDecimal.b ; True if we caught the decimal place while cleaning the text. If so, do not allow another one. Protected DecimalCount.b ; This will be a counter for the number of assumed decimal numbers in our text. We'll ; use this while cleaning the text. Protected DecimalString.s ; This will hold the numbers that are assumed to be decimals. Protected IntegerString.s ; This will hold the integer numbers. Protected IntegerCount.b ; this will be the counter for the number of integers in our text. Protected CountSeparator.b ; This will be a count for our separator character, if we are showing it. PositionText = Len(inText) ; Set the position to the end of the string. We'll move from right to left. If inText = "" ; Passed an empty string. If EditingMask = #True ; Show the plain number. If ForceDecimal = #True ; We want to show the decimal so show the appropriate number of zeros. ProcedureReturn "0." + RSet("", NumberDecimals, "0") Else ProcedureReturn "0" EndIf ; Else ; Show the number with the "$" character. If ForceDecimal = #True ; We want to show the decimal so show the appropriate number of zeros. ProcedureReturn "$0." + RSet("", NumberDecimals, "0") Else ProcedureReturn "$0" EndIf ; EndIf ; EndIf ; Repeat ; Now we will begin to clean our text. HoldTextChar = Asc(Mid(inText, PositionText, 1)) ; If HoldTextChar = 46 ; Decimal character. If CaughtDecimal = #False ; We haven't caught the decimal character yet. If DecimalCount = 0 ; We have a decimal place but no decimal numbers If ForceDecimal = #True ; We're forcing the decimal place. sReturn = "." + RSet("", NumberDecimals, "0") + sReturn ; Since we're automatically showing the decimal place but there are no ; decimal numbers after the decimal, pad the decimal with zeros. EndIf ; If forcedecimal is False, we don't want to show the decimal place at all ; because there were no numbers after the decimal place. ElseIf DecimalCount > NumberDecimals ; Our temporary decimal string contains more numbers than we're allowing. If RoundDecimal = #True ; We will round our number to the number of allowed decimals If Mid(DecimalString, NumberDecimals + 1, 1) >= "5" ; Round up sReturn = "." + Mid(DecimalString, 1, NumberDecimals - 1) + Str(Val(Mid(DecimalString, NumberDecimals, 1)) + 1) + sReturn ; Else ; Don't round. sReturn = "." + Mid(DecimalString, 1, NumberDecimals) + sReturn ; EndIf ; Else ; We will just truncate the decimals to fit the number of allowed decimals. sReturn = "." + Mid(DecimalString, 1, NumberDecimals) + sReturn ; EndIf ; ElseIf DecimalCount < NumberDecimals ; There aren't enough decimal places for what's required. sReturn = "." + DecimalString + RSet("", NumberDecimals - DecimalCount, "0") ; Now add the decimal, the decimal string and zeros to pad out what's required. Else ; The decimal numbers are the same. Add it. sReturn = "." + DecimalString ; EndIf ; EndIf ; CaughtDecimal = #True ; ElseIf HoldTextChar = 45 ; Minus (dash) character. ShowNegative = #True ; We've located a negative sign so change this to true. ElseIf HoldTextChar >= 48 And HoldTextChar <= 57 ; Numeric value. ShowNegative = #False ; No matter what, set this to false. If we've found a number after a negative sign ; the negative sign must be a junk character. If CaughtDecimal = #False ; If we haven't caught our decimal character yet, the numbers may be a decimal. DecimalCount + 1 ; Increment our decimal number count. DecimalString = Chr(HoldTextChar) + DecimalString ; And temporarily add the assumed decimal number to our decimal string. Else ; We've already caught the decimal character so the number should be an integer. IntegerString = Chr(HoldTextChar) + IntegerString ; Store the integer. CaughtInteger = #True ; We've caught a non-decimal number so let the procedure know. EndIf ; EndIf ; PositionText - 1 ; Decrement our position. We're going from right to left. Until PositionText = 0 ; If CaughtInteger = #True ; IntegerString = Str(Val(IntegerString)) ; Now remove any leading zeros. IntegerCount = Len(IntegerString) ; And hold the length of the string. If ShowSeparator = #True And EditingMask = #False And IntegerCount > 3 ; Make sure we're showing the thousands separator and there are more than 3 integers. PositionText = IntegerCount ; Repeat ; Countseparator should be 0 still. If CountSeparator = 3 : sReturn = "," + sReturn : CountSeparator = 0 : EndIf ; If we've hit 3 numbers and we're showing the separator character, add the character and reset our count. sReturn = Mid(IntegerString, PositionText, 1) + sReturn ; Now add the number to our return string. CountSeparator + 1 ; Increment our integer count. PositionText - 1 ; And decrement our position. Until PositionText = 0 ; Else ; We aren't showing the thousands separator so just add the integers to our return string. sReturn = IntegerString + sReturn ; EndIf ; Else ; If CaughtDecimal = #True ; We've found a decimal point but no numbers after the decimal. sReturn = "0" + sReturn ; Return a zero integer and the decimal number. Else ; We never found the decimal place. If ForceDecimal = #True ; We're forcing the decimal place so show zeros. If ShowSeparator = #True And EditingMask = #False And DecimalCount > 3 ; Make sure we're showing the thousands separator and there are more than 3 integers. PositionText = DecimalCount ; Repeat ; Countseparator should be 0 still. If CountSeparator = 3 : sReturn = "," + sReturn : CountSeparator = 0 : EndIf ; If we've hit 3 numbers and we're showing the separator character, add the character and reset our count. sReturn = Mid(DecimalString, PositionText, 1) + sReturn ; Now add the number to our return string. CountSeparator + 1 ; Increment our integer count. PositionText - 1 ; And decrement our position. Until PositionText = 0 ; Else ; We aren't showing the thousands separator so just add the integers to our return string. sReturn = Str(Val("0"+DecimalString)) + "." + RSet("", NumberDecimals, "0") ; EndIf ; Else ; We aren't forcing the decimal place so just return the integer. sReturn = DecimalString ; No need to add sReturn to the end since there are no decimal values. EndIf ; EndIf ; EndIf ; If EditingMask = #False : sReturn = "$" + sReturn : EndIf ; The first character will be the money sign. Possible to make this country specific? If ShowNegative = #True ; Our number is negative. If EditingMask = #False And NegativeParan = #True sReturn = "(" + sReturn + ")" Else sReturn = "-" + sReturn ; Show the negative sign. EndIf EndIf ; ProcedureReturn sReturn ; And finally return the combined string. EndProcedure Procedure.b xm_FinancialCheckAndReplace(*xMask.s_xMask, inText.s, InCharacter.b, StartSelection.l, EndSelection.l) ; Protected PositionText.l ; This will store the current position in the mask. Protected ReturnCharacter.b ; This will be the asc() value we return. Protected Length.l ; This will hold the length of our Mask. We'll check against it while looping. Protected HoldMaskChar.b ; This will temporarily hold a single mask character. We'll check this to see how to ; convert the InText to match it. Protected AtPosition.l ; This will be the virtual location in the mask. Since characters like '>', '<' and '\' ; only change the following characters and are not displayed, we will not include them ; in our position. StartSelection + 1 ; Length = Len(inText) ; Store the maximum string length - based on our mask. PositionText = 1 ; One based position AtPosition = 1 ; Set this to 1 as well. Repeat ; HoldMaskChar = Asc(Mid(inText, PositionText, 1)) ; Store the byte value of the current mask character. If HoldMaskChar = 0 ; String is empty. Allow numbers, decimals or the negative sign. If (InCharacter >= 48 And InCharacter <= 57) Or InCharacter = 45 Or InCharacter = 46 ProcedureReturn 1 Else ProcedureReturn 0 EndIf ; ElseIf HoldMaskChar >= 48 And HoldMaskChar <= 57 ; If (AtPosition = 1 And InCharacter = 45) : ProcedureReturn 1 : EndIf ; If we're at the very beginning of our line and the first character in the xMask ; gadget is a number, we'll allow the user to add a negative character. This should ; only happen if the number is not negative already. If ((InCharacter >= 48 And InCharacter <= 57) Or InCharacter = 46) And Length = EndSelection : ProcedureReturn 1 : EndIf ; If we're at the end of the field and we typed a number, automatically allow it. If AtPosition = StartSelection If (InCharacter >= 48 And InCharacter <= 57) Or InCharacter = 46 ProcedureReturn 1 Else ProcedureReturn 0 EndIf Else AtPosition + 1 EndIf ; ElseIf HoldMaskChar = 36 ; The dollar sign character ($) If AtPosition = StartSelection ProcedureReturn 0 Else AtPosition + 1 EndIf ElseIf HoldMaskChar = 44 ; The thousands separator character (,) If AtPosition = StartSelection If (InCharacter >= 48 And InCharacter <= 57) Or InCharacter = 46 ProcedureReturn 1 Else ProcedureReturn 0 EndIf Else AtPosition + 1 EndIf ElseIf HoldMaskChar = 45 ; The negative indicator character (-) If ((InCharacter >= 48 And InCharacter <= 57) Or InCharacter = 46) And Length = EndSelection : ProcedureReturn 1 : EndIf ; If we're at the end of the field and we typed a number, automatically allow it. If ((InCharacter >= 48 And InCharacter <= 57) Or InCharacter = 45 Or InCharacter = 46) And EndSelection >= StartSelection : ProcedureReturn 1 : EndIf ; We've either completely selected the negative sign or selected the negative sign as well ; as another character. Anything we type will replace this selection. Allow it if it's ; the negative sign, a number or the decimal character. If AtPosition = StartSelection ProcedureReturn 0 Else AtPosition + 1 EndIf ElseIf HoldMaskChar = 46 ; The decimal character (.) If ((InCharacter >= 48 And InCharacter <= 57) Or InCharacter = 46) And Length = EndSelection : ProcedureReturn 1 : EndIf ; If we're at the end of the field and we typed a number, automatically allow it. If AtPosition = StartSelection If (InCharacter >= 48 And InCharacter <= 57) Or InCharacter = 46 ProcedureReturn 1 Else ProcedureReturn 0 EndIf Else AtPosition + 1 EndIf EndIf ; PositionText + 1 ; Now increment our position in the mask. Until PositionText = Length + 1 ; ProcedureReturn ReturnCharacter ; And finally return the combined string. EndProcedure ;- Private xMask Functions Procedure.s xm_MaskToText(InMask.s, inText.s, PlaceHolder.b, ForcePlaceHolder.b) ; This function will convert a mask and text to a display text. For example, if InMask ; is "00000-9999" and InText is "981047004" the output will be "98104-7004". If InMask ; is "00000-9999", InText is "" (empty) and PlaceHolder is "_" the output text will be ; "_____-____". With an InMask of "00000-9999", InText of "98104" and PlaceHolder is "_" ; the return will be "98104-____". If ForcePlaceHolder is True we will fill the ; return string with our mask and placeholder. Protected PositionMask.l ; This will store the current position in the mask. Protected PositionText.l ; This will store the current position in the text. Protected sReturn.s ; This will be the combined string we return. Protected Length.l ; This will hold the length of our Mask. We'll check against it while looping. Protected HoldMaskChar.b ; This will temporarily hold a single mask character. We'll check this to see how to ; convert the InText to match it. Protected HoldTextString.s ; This will temporarily hold the Text character we want to merge. Protected HoldTextChar.b ; This is the asc() equivalent of HoldTextString. Used for faster comparisons. Protected DoUpper.b ; When True, the character is to be capitalized. Protected DoLower.b ; When False, the character is to be decapitalized. Protected DoDisplay.b ; See xm_GetMaskMaxLength() AllowAsNonFormatting variable. Protected FailedMask.b ; True if the text we're trying to merge fails our mask. False if everything is okay. Length = Len(InMask) ; Store the maximum string length - based on our mask. PositionMask = 1 ; One based position PositionText = 1 ; Ditto Repeat ; HoldMaskChar = Asc(Mid(InMask, PositionMask, 1)) ; Store the byte value of the current mask character. If DoDisplay = #False ; Handle the mask character normally. If ForcePlaceHolder = #True ; Passing an empty InText string means we want to fill the xMask gadget with the mask and placeholder. HoldTextString = Chr(PlaceHolder) HoldTextChar = PlaceHolder Else HoldTextString = Mid(inText, PositionText, 1) ; Temporarily store the character from the Text field for matching purposes. HoldTextChar = Asc(HoldTextString) ; Now store the byte value of the character. EndIf ; If HoldMaskChar = 48 ; The character is the "0" (zero) character. This is a required number field. If ForcePlaceHolder = #True sReturn = sReturn + HoldTextString Else If (HoldTextChar >= 48 And HoldTextChar <= 57) : sReturn = sReturn + HoldTextString : Else : FailedMask = #True : EndIf ; Now add in the text from our incoming text if it's a numeral. EndIf PositionText + 1 ; And increment our text position. Do this regardless of whether we match the type. DoUpper = #False ; Set this to false by default. If the previous character was a ">" (uppercase formatting), ; we'll want to only capitalize the character following the ">". DoLower = #False ; Same as above but with lowercase. ElseIf HoldMaskChar = 57 ; The character is the "9" (nine) character. This is a non-required number field. ; Either a digit or a space is allowed here. If ForcePlaceHolder = #True sReturn = sReturn + HoldTextString Else If (HoldTextChar >= 48 And HoldTextChar <= 57) Or HoldTextChar = 32 : sReturn = sReturn + HoldTextString : Else : FailedMask = #True : EndIf ; Now add in the text from our incoming text if it's a numeral. Also store it if it's just a space. EndIf PositionText + 1 ; And increment our text position. Do this regardless of whether we match the type. DoUpper = #False ; Set this to false by default. If the previous character was a ">" (uppercase formatting), ; we'll want to only capitalize the character following the ">". DoLower = #False ; Same as above but with lowercase. ElseIf HoldMaskChar = 76 ; The "L" (uppercase L) character. A - Z, entry required If ForcePlaceHolder = #True sReturn = sReturn + HoldTextString Else If (HoldTextChar >= 65 And HoldTextChar <= 90) Or (HoldTextChar >= 97 And HoldTextChar <= 122) ; The text is an alpha character. If DoUpper = #True And (HoldTextChar >= 97 And HoldTextChar <= 122) ; The letter is lowercase. Capitalize it. sReturn = sReturn + UCase(HoldTextString) ; Add the uppercase character. ElseIf DoLower = #True And (HoldTextChar >= 65 And HoldTextChar <= 90) ; The letter is uppercase. Decapitalize it. sReturn = sReturn + LCase(HoldTextString) ; Add the lowercase character. Else ; sReturn = sReturn + HoldTextString ; Add the character normally. EndIf ; Else FailedMask = #True EndIf ; Now add in the text from our incoming text if it's a numeral. EndIf ; PositionText + 1 ; And increment our text position. Do this regardless of whether we match the type. DoUpper = #False ; Set this to false by default. If the previous character was a ">" (uppercase formatting), ; we'll want to only capitalize the character following the ">". DoLower = #False ; Same as above but with lowercase. ElseIf HoldMaskChar = 63 ; The "?" character. A - Z, entry optional. If ForcePlaceHolder = #True sReturn = sReturn + HoldTextString Else If (HoldTextChar >= 65 And HoldTextChar <= 90) Or (HoldTextChar >= 97 And HoldTextChar <= 122) Or HoldTextChar = 32 ; The character is an alpha or an empty space. If HoldTextString = " " ; sReturn = sReturn + HoldTextString ; Add the space normally. ElseIf DoUpper = #True And (HoldTextChar >= 97 And HoldTextChar <= 122) ; The letter is lowercase. Capitalize it. sReturn = sReturn + UCase(HoldTextString) ; Add the uppercase character. ElseIf DoLower = #True And (HoldTextChar >= 65 And HoldTextChar <= 90) ; The letter is uppercase. Decapitalize it. sReturn = sReturn + LCase(HoldTextString) ; Add the lowercase character. Else ; sReturn = sReturn + HoldTextString ; Add the character normally. EndIf ; Else FailedMask = #True EndIf ; Now add in the text from our incoming text if it's a numeral. EndIf ; PositionText + 1 ; And increment our text position. Do this regardless of whether we match the type. DoUpper = #False ; Set this to false by default. If the previous character was a ">" (uppercase formatting), ; we'll want to only capitalize the character following the ">". DoLower = #False ; Same as above but with lowercase. ElseIf HoldMaskChar = 65 ; The "A" character. A - Z or 0 - 9, entry required. If ForcePlaceHolder = #True sReturn = sReturn + HoldTextString Else If (HoldTextChar >= 65 And HoldTextChar <= 90) Or (HoldTextChar >= 97 And HoldTextChar <= 122) Or (HoldTextChar >= 48 And HoldTextChar <= 57) ; Test our character against the mask. If DoUpper = #True And (HoldTextChar >= 97 And HoldTextChar <= 122) ; The letter is lowercase. Capitalize it. sReturn = sReturn + UCase(HoldTextString) ; Add the uppercase character. ElseIf DoLower = #True And (HoldTextChar >= 65 And HoldTextChar <= 90) ; The letter is uppercase. Decapitalize it. sReturn = sReturn + LCase(HoldTextString) ; Add the lowercase character. Else ; sReturn = sReturn + HoldTextString ; Add the character normally. EndIf ; Else FailedMask = #True EndIf ; Now add in the text from our incoming text if it's a numeral. EndIf ; PositionText + 1 ; And increment our text position. Do this regardless of whether we match the type. DoUpper = #False ; Set this to false by default. If the previous character was a ">" (uppercase formatting), ; we'll want to only capitalize the character following the ">". DoLower = #False ; Same as above but with lowercase. ElseIf HoldMaskChar = 97 ; The "a" character. A - Z or 0 - 9, entry optional. If ForcePlaceHolder = #True sReturn = sReturn + HoldTextString Else If (HoldTextChar >= 65 And HoldTextChar <= 90) Or (HoldTextChar >= 97 And HoldTextChar <= 122) Or (HoldTextChar >= 48 And HoldTextChar <= 57) Or HoldTextChar = 32 ; If HoldTextString = " " ; sReturn = sReturn + HoldTextString ; ElseIf DoUpper = #True And (HoldTextChar >= 97 And HoldTextChar <= 122) ; The letter is lowercase. Capitalize it. sReturn = sReturn + UCase(HoldTextString) ; Add the uppercase character. ElseIf DoLower = #True And (HoldTextChar >= 65 And HoldTextChar <= 90) ; The letter is uppercase. Decapitalize it. sReturn = sReturn + LCase(HoldTextString) ; Add the lowercase character. Else ; sReturn = sReturn + HoldTextString ; Add the character normally. EndIf ; Else FailedMask = #True EndIf ; Now add in the text from our incoming text if it's a numeral. EndIf ; PositionText + 1 ; And increment our text position. Do this regardless of whether we match the type. DoUpper = #False ; Set this to false by default. If the previous character was a ">" (uppercase formatting), ; we'll want to only capitalize the character following the ">". DoLower = #False ; Same as above but with lowercase. ElseIf HoldMaskChar = 38 ; The "&" character. Any character or space, entry required. If ForcePlaceHolder = #True sReturn = sReturn + HoldTextString Else If HoldTextChar >= 33 And HoldTextChar <= 255 ; If DoUpper = #True And (HoldTextChar >= 97 And HoldTextChar <= 122) ; The letter is lowercase. Capitalize it. sReturn = sReturn + UCase(HoldTextString) ; Add the uppercase character. ElseIf DoLower = #True And (HoldTextChar >= 65 And HoldTextChar <= 90) ; The letter is uppercase. Decapitalize it. sReturn = sReturn + LCase(HoldTextString) ; Add the lowercase character. Else ; sReturn = sReturn + HoldTextString ; Add the character normally. EndIf ; Else FailedMask = #True EndIf ; Now add in the text from our incoming text if it's a numeral. EndIf ; PositionText + 1 ; And increment our text position. Do this regardless of whether we match the type. DoUpper = #False ; Set this to false by default. If the previous character was a ">" (uppercase formatting), ; we'll want to only capitalize the character following the ">". DoLower = #False ; Same as above but with lowercase. ElseIf HoldMaskChar = 67 ; The "C" character. Any character or space, entry optional. If ForcePlaceHolder = #True sReturn = sReturn + HoldTextString Else If HoldTextChar >= 32 And HoldTextChar <= 255 ; If HoldTextString = " " ; sReturn = sReturn + HoldTextString ; ElseIf DoUpper = #True And (HoldTextChar >= 97 And HoldTextChar <= 122) ; The letter is lowercase. Capitalize it. sReturn = sReturn + UCase(HoldTextString) ; Add the uppercase character. ElseIf DoLower = #True And (HoldTextChar >= 65 And HoldTextChar <= 90) ; The letter is uppercase. Decapitalize it. sReturn = sReturn + LCase(HoldTextString) ; Add the lowercase character. Else ; sReturn = sReturn + HoldTextString ; Add the character normally. EndIf ; Else FailedMask = #True EndIf ; Now add in the text from our incoming text if it's a numeral. EndIf ; PositionText + 1 ; And increment our text position. Do this regardless of whether we match the type. DoUpper = #False ; Set this to false by default. If the previous character was a ">" (uppercase formatting), ; we'll want to only capitalize the character following the ">". DoLower = #False ; Same as above but with lowercase. ElseIf HoldMaskChar = 62 ; The ">" character for capitalization. DoUpper = #True DoLower = #False ElseIf HoldMaskChar = 60 ; The "<" character for decapitalization. DoUpper = #False DoLower = #True ElseIf HoldMaskChar = 92 ; The "\" character. Forces any character to be used as a display character. DoDisplay = #True ElseIf HoldMaskChar = 32 ; An empty text field (" "). sReturn = sReturn + Chr(HoldMaskChar) ; Add the empty space as-is. It's a kind of display character. Like the space ; in "(123) 456-7890". We want to keep it there. Just skip over it. Else ; The character was a non-formating character. Add it as-is. sReturn = sReturn + Chr(HoldMaskChar) ; Add the formatting character into our string. An example of this would be the ; dash in a US extended zip code or the dashes for a credit card number. EndIf ; Else ; The last character was a '\' character - force the current mask character to be displayed. sReturn = sReturn + Chr(HoldMaskChar) ; Add the character as a display character. DoDisplay = #False ; And reset our DoDisplay variable back to false. EndIf ; PositionMask + 1 ; Now increment our position in the mask. Until PositionMask = Length + 1 ; If FailedMask = #True : sReturn = "" : EndIf ; If we failed our mask conditions because of some bad text, empty our return string. ProcedureReturn sReturn ; And finally return the combined string. EndProcedure Procedure.s xm_ReturnText(*xMask.s_xMask, inText.s) ; Protected PositionMask.l ; This will store the current position in the mask. Protected PositionText.l ; This will store the current position in the text. Protected sReturn.s ; This will be the combined string we return. Protected Length.l ; This will hold the length of our Mask. We'll check against it while looping. Protected HoldMaskChar.b ; This will temporarily hold a single mask character. We'll check this to see how to ; convert the InText to match it. Protected HoldTextString.s ; This will temporarily hold the Text character we want to merge. Protected HoldTextChar.b ; This is the asc() equivalent of HoldTextString. Used for faster comparisons. Protected DoDisplay.b ; See xm_GetMaskMaxLength() AllowAsNonFormatting variable. Protected FailedMask.b ; True if the text we're trying to merge fails our mask. False if everything is okay. Length = Len(*xMask\Mask) ; Store the maximum string length - based on our mask. PositionMask = 1 ; One based position PositionText = 1 ; Ditto Repeat ; HoldMaskChar = Asc(Mid(*xMask\Mask, PositionMask, 1)) ; Store the byte value of the current mask character. If DoDisplay = #False ; Handle the mask character normally. HoldTextString = Mid(inText, PositionText, 1) ; Temporarily store the character from the Text field for matching purposes. HoldTextChar = Asc(HoldTextString) ; Now store the byte value of the character. If HoldMaskChar = 48 ; The character is the "0" (zero) character. This is a required number field. If (HoldTextChar >= 48 And HoldTextChar <= 57) : sReturn = sReturn + HoldTextString : EndIf ; Now add in the text from our incoming text if it's a numeral. ElseIf HoldMaskChar = 57 ; The character is the "9" (nine) character. This is a non-required number field. ; Either a digit or a space is allowed here. If (HoldTextChar >= 48 And HoldTextChar <= 57) Or HoldTextChar = 32 : sReturn = sReturn + HoldTextString : EndIf ; Now add in the text from our incoming text if it's a numeral. Also store it if it's just a space. ElseIf HoldMaskChar = 76 ; The "L" (uppercase L) character. A - Z, entry required If (HoldTextChar >= 65 And HoldTextChar <= 90) Or (HoldTextChar >= 97 And HoldTextChar <= 122) ; The text is an alpha character. sReturn = sReturn + HoldTextString ; Add the character normally. EndIf ; ElseIf HoldMaskChar = 63 ; The "?" character. A - Z, entry optional. If (HoldTextChar >= 65 And HoldTextChar <= 90) Or (HoldTextChar >= 97 And HoldTextChar <= 122) Or HoldTextChar = 32 ; The character is an alpha or an empty space. If HoldTextString = " " ; sReturn = sReturn + HoldTextString ; Add the space normally. Else ; sReturn = sReturn + HoldTextString ; Add the character normally. EndIf ; Now add in the text from our incoming text if it's a numeral. EndIf ; ElseIf HoldMaskChar = 65 ; The "A" character. A - Z or 0 - 9, entry required. If (HoldTextChar >= 65 And HoldTextChar <= 90) Or (HoldTextChar >= 97 And HoldTextChar <= 122) Or (HoldTextChar >= 48 And HoldTextChar <= 57) ; Test our character against the mask. sReturn = sReturn + HoldTextString ; Now add in the text from our incoming text if it's a numeral. EndIf ; ElseIf HoldMaskChar = 97 ; The "a" character. A - Z or 0 - 9, entry optional. If (HoldTextChar >= 65 And HoldTextChar <= 90) Or (HoldTextChar >= 97 And HoldTextChar <= 122) Or (HoldTextChar >= 48 And HoldTextChar <= 57) Or HoldTextChar = 32 ; If HoldTextString = " " ; sReturn = sReturn + HoldTextString ; Else ; sReturn = sReturn + HoldTextString ; Add the character normally. EndIf ; Now add in the text from our incoming text if it's a numeral. EndIf ; ElseIf HoldMaskChar = 38 ; The "&" character. Any character or space, entry required. If HoldTextChar >= 33 And HoldTextChar <= 255 ; sReturn = sReturn + HoldTextString ; Now add in the text from our incoming text if it's a numeral. EndIf ; ElseIf HoldMaskChar = 67 ; The "C" character. Any character or space, entry optional. If HoldTextChar >= 32 And HoldTextChar <= 255 ; If HoldTextString = " " ; sReturn = sReturn + HoldTextString ; Else ; sReturn = sReturn + HoldTextString ; Add the character normally. EndIf ; Now add in the text from our incoming text if it's a numeral. EndIf ; ElseIf HoldMaskChar = 62 ; The ">" character for capitalization. ElseIf HoldMaskChar = 60 ; The "<" character for decapitalization. ElseIf HoldMaskChar = 92 ; The "\" character. Forces any character to be used as a display character. DoDisplay = #True ElseIf HoldMaskChar = 32 ; An empty text field (" "). sReturn = sReturn + Chr(HoldMaskChar) ; Add the empty space as-is. It's a kind of display character. Like the space ; in "(123) 456-7890". We want to keep it there. Just skip over it. Else ; The character was a non-formating character. Add it as-is. sReturn = sReturn + Chr(HoldMaskChar) ; Add the formatting character into our string. An example of this would be the ; dash in a US extended zip code or the dashes for a credit card number. EndIf ; Else ; The last character was a '\' character - force the current mask character to be displayed. sReturn = sReturn + Chr(HoldMaskChar) ; Add the character as a display character. DoDisplay = #False ; And reset our DoDisplay variable back to false. EndIf ; PositionText + 1 ; PositionMask + 1 ; Now increment our position in the mask. Until PositionMask = Length + 1 ; ProcedureReturn sReturn ; And finally return the combined string. EndProcedure Procedure.l xm_GetMaskMaxLength(InMask.s) ; This will return the actual maximum length of the mask. This does not include formatting ; characters such as ">" to capitalize a word or "/" to allow a formatting character as a ; non formatting character. Protected PositionMask.l ; This will store the current position in the mask. Protected ReturnLength.l ; This will be the maximum allowed length for the mask. Protected HoldMaskChar.b ; This will temporarily hold a single mask character. We'll check this to see how to ; convert the InText to match it. Protected AllowAsNonFormatting.b ; This is used to allow formatting characters to be used as non-formatting characters. ; If we encounter a '\' character the following character will be allowed as a non ; formatted character. Examples - '\A' will return a non-formatting 'A' in the displayed ; string. '\\\0' will return a non-formatting "\0" in the displayed string. This is ; because of the first "\" character enables the next character ("\") as a display ; character. The third "\" enables the "0" as a display character. If InMask = "#Financial" ; If we're using a special case financial mask, set limit and exit. Not exactly sure ; what limit to set for this yet. ProcedureReturn 20 ; Guess for now. EndIf ; PositionMask = 1 ; One based position. Length = Len(InMask) ; Store the maximum string length - based on our mask. Repeat ; HoldMaskChar = Asc(Mid(InMask, PositionMask, 1)) ; Store the byte value of the current mask character. If AllowAsNonFormatting = #False ; The current character will be treated as a normal formatting character. If HoldMaskChar = 48 ; The character is the "0" (zero) character. This is a required number field. ReturnLength + 1 ElseIf HoldMaskChar = 57 ; The character is the "9" (nine) character. This is a non-required number field. ; Either a digit or a space is allowed here. ReturnLength + 1 ElseIf HoldMaskChar = 76 ; The "L" (uppercase L) character. A - Z, entry required ReturnLength + 1 ElseIf HoldMaskChar = 63 ; The "?" character. A - Z, entry optional. ReturnLength + 1 ElseIf HoldMaskChar = 65 ; The "A" character. A - Z or 0 - 9, entry required. ReturnLength + 1 ElseIf HoldMaskChar = 97 ; The "a" character. A - Z or 0 - 9, entry optional. ReturnLength + 1 ElseIf HoldMaskChar = 38 ; The "&" character. Any character or space, entry required. ReturnLength + 1 ElseIf HoldMaskChar = 67 ; The "C" character. Any character or space, entry optional. ReturnLength + 1 ElseIf HoldMaskChar = 0 ; A null text field (""). ElseIf HoldMaskChar = 32 ; An empty text field (" "). ElseIf HoldMaskChar = 92 ; The 'force non-formatting character' character ("\"). Ignore this character ; and do not increase our maximum length. AllowAsNonFormatting = #True ; Now the following character will be included in the display no matter what it is. Else ; The character was a non-formating character. Add it as-is. ReturnLength + 1 ; A display character always increases our length. EndIf ; Else ; The previous character was a "\" character. This will force the current character ; to be used as a display character. ReturnLength + 1 ; No matter what, increase our length. The previous character forces this. AllowAsNonFormatting = #False ; Now disable the non-formatting test. We'll wait for another "\" before ; enabling this again. EndIf ; PositionMask + 1 ; Until PositionMask = Length + 1 ; ProcedureReturn ReturnLength ; And finally return the combined string. EndProcedure Procedure.b xm_CheckAndReplace(*xMask.s_xMask, InCharacter.b, *StartSelection.l, *EndSelection.l, AsPlaceHolder.b) ; This code will search our Mask, locate the position in the mask based on the selection and ; return the character value to replace the selection with. For instance, if we passed a ; lower-case "a" (chr = 97) and the mask is ">L" we will return 65. This is the chr code for ; the upper-case "A" which is what our mask is doing. AsPlaceHolder is a boolean variable. ; If it is True, we're replacing the text with the placeholder character. We automatically ; want this to succeed unless we're trying to insert the placeholder into a display character. Protected PositionMask.l ; This will store the current position in the mask. Protected ReturnCharacter.b ; This will be the asc() value we return. Protected Length.l ; This will hold the length of our Mask. We'll check against it while looping. Protected HoldMaskChar.b ; This will temporarily hold a single mask character. We'll check this to see how to ; convert the InText to match it. Protected DoUpper.b ; When True, the character is to be capitalized. Protected DoLower.b ; When False, the character is to be decapitalized. Protected DoDisplay.b ; See xm_GetMaskMaxLength() AllowAsNonFormatting variable. Protected AtPosition.l ; This will be the virtual location in the mask. Since characters like '>', '<' and '\' ; only change the following characters and are not displayed, we will not include them ; in our position. Protected HoldStart.l Protected HoldEnd.l ; These will temporarily hold the start and end selection values. HoldStart = PeekL(*StartSelection) + 1 HoldEnd = PeekL(*EndSelection) + 1 ; Temporarily store the start and end selection. We're working with 1 based indexes because of ; the Mid() function so increment our start & end position. Length = Len(*xMask\Mask) ; Store the maximum string length - based on our mask. PositionMask = 1 ; One based position AtPosition = 1 ; Set this to 1 as well. Repeat ; HoldMaskChar = Asc(Mid(*xMask\Mask, PositionMask, 1)) ; Store the byte value of the current mask character. If DoDisplay = #False ; The current character will be treated as a normal formatting character. If HoldMaskChar = 48 ; The character is the "0" (zero) character. This is a required number field. If AtPosition = HoldStart ; We've located the position in our mask that matches our selection start. If (InCharacter >= 48 And InCharacter <= 57) Or AsPlaceHolder = #True ; Since we're expecting a number, test for it. PokeL(*EndSelection, AtPosition) ; For non-selected positions the end selection is the same as the start selection. We'll ; need to change that if we're going to replace the character. So, set the end selection ; to the AtPosition variable. Since AtPosition is 1 based, it will be 1 more than the ; start position originally passed to the procedure. ProcedureReturn InCharacter ; We're entering a number so everything is good. Pass back the number. Else ProcedureReturn 0 ; Return a zero. We're expecting a numeral. EndIf ; EndIf ; AtPosition + 1 ; This was not the mask position we needed. Increment our virtual position and continue. DoUpper = #False ; Set this to false by default. If the previous character was a ">" (uppercase formatting), ; we'll want to only capitalize the character following the ">". DoLower = #False ; Same as above but with lowercase. ElseIf HoldMaskChar = 57 ; The character is the "9" (nine) character. This is a non-required number field. Either a ; digit or a space is allowed here. If AtPosition = HoldStart ; We've located the position in our mask that matches our selection start. If (InCharacter >= 48 And InCharacter <= 57) Or InCharacter = 32 Or AsPlaceHolder = #True ; Since we're expecting a number, test for it. PokeL(*EndSelection, AtPosition) ; For non-selected positions the end selection is the same as the start selection. We'll ; need to change that if we're going to replace the character. So, set the end selection ; to the AtPosition variable. Since AtPosition is 1 based, it will be 1 more than the ; start position originally passed to the procedure. ProcedureReturn InCharacter ; We're entering a number so everything is good. Pass back the number. Else ProcedureReturn 0 ; Return a zero. We're expecting a numeral. EndIf ; EndIf ; AtPosition + 1 ; This was not the mask position we needed. Increment our virtual position and continue. DoUpper = #False ; Set this to false by default. If the previous character was a ">" (uppercase formatting), ; we'll want to only capitalize the character following the ">". DoLower = #False ; Same as above but with lowercase. ElseIf HoldMaskChar = 76 ; The "L" character. This is a required alpha character field. If AtPosition = HoldStart ; We've located the position in our mask that matches our selection start. If (InCharacter >= 65 And InCharacter <= 90) Or (InCharacter >= 97 And InCharacter <= 122) Or AsPlaceHolder = #True ; Since we're expecting a letter, test for it. PokeL(*EndSelection, AtPosition) ; For non-selected positions the end selection is the same as the start selection. We'll ; need to change that if we're going to replace the character. So, set the end selection ; to the AtPosition variable. Since AtPosition is 1 based, it will be 1 more than the ; start position originally passed to the procedure. If AsPlaceHolder = #True ProcedureReturn InCharacter ; We're entering a letter so everything is good. Pass back the letter. ElseIf DoUpper = #True And (InCharacter >= 97 And InCharacter <= 122) ; Change any lowercase character to uppercase. ProcedureReturn InCharacter - 32 ; 'a' is 97, 'A' is 65 - the difference is -32. ElseIf DoLower = #True And (InCharacter >= 65 And InCharacter <= 90) ; Change any uppercase character to lowercase. ProcedureReturn InCharacter + 32 ; 'A' is 65, 'a' is 97 - the difference is +32. Else ProcedureReturn InCharacter ; We're entering a letter so everything is good. Pass back the letter. EndIf ; Else ProcedureReturn 0 ; Return a zero. We're expecting a letter. EndIf ; EndIf ; AtPosition + 1 ; This was not the mask position we needed. Increment our virtual position and continue. DoUpper = #False ; Set this to false by default. If the previous character was a ">" (uppercase formatting), ; we'll want to only capitalize the character following the ">". DoLower = #False ; Same as above but with lowercase. ElseIf HoldMaskChar = 63 ; The "?" character. A - Z, entry optional. If AtPosition = HoldStart ; We've located the position in our mask that matches our selection start. If (InCharacter >= 65 And InCharacter <= 90) Or (InCharacter >= 97 And InCharacter <= 122) Or InCharacter = 32 Or AsPlaceHolder = #True ; Since we're expecting a letter, test for it. Also allow blanks. PokeL(*EndSelection, AtPosition) ; For non-selected positions the end selection is the same as the start selection. We'll ; need to change that if we're going to replace the character. So, set the end selection ; to the AtPosition variable. Since AtPosition is 1 based, it will be 1 more than the ; start position originally passed to the procedure. If AsPlaceHolder = #True ProcedureReturn InCharacter ; We're entering a letter so everything is good. Pass back the letter. ElseIf DoUpper = #True And (InCharacter >= 97 And InCharacter <= 122) ; Change any lowercase character to uppercase. ProcedureReturn InCharacter - 32 ; 'a' is 97, 'A' is 65 - the difference is -32. ElseIf DoLower = #True And (InCharacter >= 65 And InCharacter <= 90) ; Change any uppercase character to lowercase. ProcedureReturn InCharacter + 32 ; 'A' is 65, 'a' is 97 - the difference is +32. Else ProcedureReturn InCharacter ; We're entering a letter so everything is good. Pass back the letter. EndIf Else ProcedureReturn 0 ; Return a zero. We're expecting a letter or a blank. EndIf ; EndIf ; AtPosition + 1 ; This was not the mask position we needed. Increment our virtual position and continue. DoUpper = #False ; Set this to false by default. If the previous character was a ">" (uppercase formatting), ; we'll want to only capitalize the character following the ">". DoLower = #False ; Same as above but with lowercase. ElseIf HoldMaskChar = 65 ; The "A" character. A - Z or 0 - 9, entry required. If AtPosition = HoldStart ; We've located the position in our mask that matches our selection start. If (InCharacter >= 65 And InCharacter <= 90) Or (InCharacter >= 97 And InCharacter <= 122) Or (InCharacter >= 48 And InCharacter <= 57) Or AsPlaceHolder = #True ; Since we're expecting a letter or a number, test for it. PokeL(*EndSelection, AtPosition) ; For non-selected positions the end selection is the same as the start selection. We'll ; need to change that if we're going to replace the character. So, set the end selection ; to the AtPosition variable. Since AtPosition is 1 based, it will be 1 more than the ; start position originally passed to the procedure. If AsPlaceHolder = #True ProcedureReturn InCharacter ; We're entering a letter so everything is good. Pass back the letter. ElseIf DoUpper = #True And (InCharacter >= 97 And InCharacter <= 122) ; Change any lowercase character to uppercase. ProcedureReturn InCharacter - 32 ; 'a' is 97, 'A' is 65 - the difference is -32. ElseIf DoLower = #True And (InCharacter >= 65 And InCharacter <= 90) ; Change any uppercase character to lowercase. ProcedureReturn InCharacter + 32 ; 'A' is 65, 'a' is 97 - the difference is +32. Else ProcedureReturn InCharacter ; We're entering a letter so everything is good. Pass back the letter. EndIf Else ProcedureReturn 0 ; Return a zero. We're expecting a letter or a number. EndIf ; EndIf ; AtPosition + 1 ; This was not the mask position we needed. Increment our virtual position and continue. DoUpper = #False ; Set this to false by default. If the previous character was a ">" (uppercase formatting), ; we'll want to only capitalize the character following the ">". DoLower = #False ; Same as above but with lowercase. ElseIf HoldMaskChar = 97 ; The "a" character. A - Z or 0 - 9, entry optional. If AtPosition = HoldStart ; We've located the position in our mask that matches our selection start. If (InCharacter >= 65 And InCharacter <= 90) Or (InCharacter >= 97 And InCharacter <= 122) Or (InCharacter >= 48 And InCharacter <= 57) Or InCharacter = 32 Or AsPlaceHolder = #True ; Since we're expecting a letter or a number, test for it. PokeL(*EndSelection, AtPosition) ; For non-selected positions the end selection is the same as the start selection. We'll ; need to change that if we're going to replace the character. So, set the end selection ; to the AtPosition variable. Since AtPosition is 1 based, it will be 1 more than the ; start position originally passed to the procedure. If AsPlaceHolder = #True ProcedureReturn InCharacter ; We're entering a letter so everything is good. Pass back the letter. ElseIf DoUpper = #True And (InCharacter >= 97 And InCharacter <= 122) ; Change any lowercase character to uppercase. ProcedureReturn InCharacter - 32 ; 'a' is 97, 'A' is 65 - the difference is -32. ElseIf DoLower = #True And (InCharacter >= 65 And InCharacter <= 90) ; Change any uppercase character to lowercase. ProcedureReturn InCharacter + 32 ; 'A' is 65, 'a' is 97 - the difference is +32. Else ProcedureReturn InCharacter ; We're entering a letter so everything is good. Pass back the letter. EndIf Else ProcedureReturn 0 ; Return a zero. We're expecting a letter or a number. EndIf ; EndIf ; AtPosition + 1 ; This was not the mask position we needed. Increment our virtual position and continue. DoUpper = #False ; Set this to false by default. If the previous character was a ">" (uppercase formatting), ; we'll want to only capitalize the character following the ">". DoLower = #False ; Same as above but with lowercase. ElseIf HoldMaskChar = 38 ; The "&" character. Any character or space, entry required. If AtPosition = HoldStart ; We've located the position in our mask that matches our selection start. If (InCharacter >= 33 And InCharacter <= 255) Or AsPlaceHolder = #True ; Test for our condition. PokeL(*EndSelection, AtPosition) ; For non-selected positions the end selection is the same as the start selection. We'll ; need to change that if we're going to replace the character. So, set the end selection ; to the AtPosition variable. Since AtPosition is 1 based, it will be 1 more than the ; start position originally passed to the procedure. If AsPlaceHolder = #True ProcedureReturn InCharacter ; We're entering a letter so everything is good. Pass back the letter. ElseIf DoUpper = #True And (InCharacter >= 97 And InCharacter <= 122) ; Change any lowercase character to uppercase. ProcedureReturn InCharacter - 32 ; 'a' is 97, 'A' is 65 - the difference is -32. ElseIf DoLower = #True And (InCharacter >= 65 And InCharacter <= 90) ; Change any uppercase character to lowercase. ProcedureReturn InCharacter + 32 ; 'A' is 65, 'a' is 97 - the difference is +32. Else ProcedureReturn InCharacter ; We're entering a letter so everything is good. Pass back the letter. EndIf Else ProcedureReturn 0 ; Return a zero. We're expecting a letter or a number. EndIf ; EndIf ; AtPosition + 1 ; This was not the mask position we needed. Increment our virtual position and continue. DoUpper = #False ; Set this to false by default. If the previous character was a ">" (uppercase formatting), ; we'll want to only capitalize the character following the ">". DoLower = #False ; Same as above but with lowercase. ElseIf HoldMaskChar = 67 ; The "C" character. Any character or space, entry optional. If AtPosition = HoldStart ; We've located the position in our mask that matches our selection start. If (InCharacter >= 32 And InCharacter <= 255) Or AsPlaceHolder = #True ; Test for our condition. PokeL(*EndSelection, AtPosition) ; For non-selected positions the end selection is the same as the start selection. We'll ; need to change that if we're going to replace the character. So, set the end selection ; to the AtPosition variable. Since AtPosition is 1 based, it will be 1 more than the ; start position originally passed to the procedure. If AsPlaceHolder = #True ProcedureReturn InCharacter ; We're entering a letter so everything is good. Pass back the letter. ElseIf DoUpper = #True And (InCharacter >= 97 And InCharacter <= 122) ; Change any lowercase character to uppercase. ProcedureReturn InCharacter - 32 ; 'a' is 97, 'A' is 65 - the difference is -32. ElseIf DoLower = #True And (InCharacter >= 65 And InCharacter <= 90) ; Change any uppercase character to lowercase. ProcedureReturn InCharacter + 32 ; 'A' is 65, 'a' is 97 - the difference is +32. Else ProcedureReturn InCharacter ; We're entering a letter so everything is good. Pass back the letter. EndIf Else ProcedureReturn 0 ; Return a zero. We're expecting a letter or a number. EndIf ; EndIf ; AtPosition + 1 ; This was not the mask position we needed. Increment our virtual position and continue. DoUpper = #False ; Set this to false by default. If the previous character was a ">" (uppercase formatting), ; we'll want to only capitalize the character following the ">". DoLower = #False ; Same as above but with lowercase. ElseIf HoldMaskChar = 32 ; An empty text field (" "). If AtPosition = HoldStart ; We've located the position in our mask that matches our selection start. Since ; a display character is here, we need to skip over it. PokeL(*StartSelection, PeekL(*StartSelection) + 1) ; We're skipping the display character so change our selection start. If AsPlaceHolder = #True ; Normally, when we type in a character we want to skip over the display characters ; and keep typing. However, if we're inserting a placeholder manually, we're doing ; it at the single position only. We do not want it to carry over into the next ; position. ProcedureReturn 0 ; So send a bad character return so we don't skip the display character. EndIf ; AtPosition + 1 ; Also change our AtPosition so we skip over it. HoldStart + 1 ; And change our held start position. Else ; We're at a display character but it's not our target position... AtPosition + 1 ; ...so just increment our position counter. EndIf ; ElseIf HoldMaskChar = 62 ; The upper-case formatting character (">"). DoUpper = #True DoLower = #False ; ElseIf HoldMaskChar = 60 ; The lower-case formatting character ("<"). DoUpper = #False DoLower = #True ; ElseIf HoldMaskChar = 92 ; The 'force non-formatting character' character ("\"). DoDisplay = #True ; Now the following character will be included in the display no matter what it is. ; Also, the character is not display so don't increment our position variables. Else ; The character was a non-formating character. An example of this would be the ; dashes "-" in a credit card mask. If AtPosition = HoldStart ; We've located the position in our mask that matches our selection start. Since ; a display character is here, we need to skip over it. PokeL(*StartSelection, PeekL(*StartSelection) + 1) ; We're skipping the display character so change our selection start. If AsPlaceHolder = #True ; Normally, when we type in a character we want to skip over the display characters ; and keep typing. However, if we're inserting a placeholder manually, we're doing ; it at the single position only. We do not want it to carry over into the next ; position. ProcedureReturn 0 ; So send a bad character return so we don't skip the display character. EndIf ; AtPosition + 1 ; Also change our AtPosition so we skip over it. HoldStart + 1 ; And change our held start position. Else ; We're at a display character but it's not our target position... AtPosition + 1 ; ...so just increment our position counter. EndIf ; EndIf ; Else ; The previous character was a "\" character. This will force the current character ; to be used as a display character. If AtPosition = HoldStart ; We've located the position in our mask that matches our selection start. Since ; a display character is here, we need to skip over it. PokeL(*StartSelection, PeekL(*StartSelection) + 1) ; We're skipping the display character so change our selection start. If AsPlaceHolder = #True ; Normally, when we type in a character we want to skip over the display characters ; and keep typing. However, if we're inserting a placeholder manually, we're doing ; it at the single position only. We do not want it to carry over into the next ; position. ProcedureReturn 0 ; So send a bad character return so we don't skip the display character. EndIf ; AtPosition + 1 ; Also change our AtPosition so we skip over it. HoldStart + 1 ; And change our held start position. Else ; We're at a display character but it's not our target position... AtPosition + 1 ; ...so just increment our position counter. EndIf ; DoDisplay = #False ; Now disable the non-formatting test. We'll wait for another "\" before ; enabling this again. EndIf ; PositionMask + 1 ; Now increment our position in the mask. Until PositionMask = Length + 1 ; ProcedureReturn ReturnCharacter ; And finally return the combined string. EndProcedure Procedure.l xm_xMaskFromHandle(InHandle.l) ; This will return a pointer to an xMask structure based on it's xMask gadget handle. ResetList(xmTrack()) While NextElement(xmTrack()) If xmTrack()\handleGadget = InHandle : ProcedureReturn = xmTrack()\idPointer : EndIf Wend ; This previous block of code will search our xmTrack() linked list for the xMask ; gadget that called the callback procedure. ProcedureReturn 0 ; Did not find the xMask gadget. EndProcedure Procedure.l xm_xMaskFromGadgetID(inGadgetID.l) ; This will return a pointer to an xMask structure based on it's xMask gadget handle. ResetList(xmTrack()) While NextElement(xmTrack()) If xmTrack()\idGadget = inGadgetID : ProcedureReturn = xmTrack()\idPointer : EndIf Wend ; This previous block of code will search our xmTrack() linked list for the xMask ; gadget that matches the gadget id. ProcedureReturn 0 ; Did not find the xMask gadget. EndProcedure ;- Public xMask Functions - Procedure.l xmGadgetHandle(inGadgetID.l) ; This will return the handle of the xMask gadget. Use it for your own custom procedures if you like. ; Perhaps some color thingies or what not. Protected *xMask.s_xMask ; This will hold the information for our xMask gadget. *xMask = xm_xMaskFromGadgetID(inGadgetID) ; Retrieve the pointer to our xMask gadget. If *xMask : ProcedureReturn *xMask\handleGadget : Else : ProcedureReturn 0 : EndIf ; Return the xMask handle if we found our xMask gadget. EndProcedure Procedure xmResizeGadget(inGadgetID, NewX.l, NewY.l, NewWidth.l, NewHeight.l) ; Pass -1 values to leave the values unchanged. ; Call this to resize our xMask gadget. Protected *xMask.s_xMask ; This will hold the information for our xMask gadget. *xMask = xm_xMaskFromGadgetID(inGadgetID) ; Retrieve the pointer to our xMask gadget. If *xMask ; Make sure we found the xMask gadget. If NewX = -1 : NewX = *xMask\x : EndIf If NewY = -1 : NewY = *xMask\y : EndIf If NewWidth = -1 : NewWidth = *xMask\Width : EndIf If NewHeight = -1 : NewHeight = *xMask\Height : EndIf ; Check for any -1 values and if found, update the passed values with the original values. This will leave them unchanged. MoveWindow_(*xMask\handleGadget, NewX, NewY, NewWidth, NewHeight, #True) ; Now move and/or resize our xMask gadget. EndIf ; EndProcedure Procedure xmActivate(inGadgetID.l, SelectAll.b) ; This will set the focus to the specified xMask gadget. Protected *xMask.s_xMask ; This will hold the information for our xMask gadget. Protected LineLength.l ; Hold the length of the text in the xMask gadget. *xMask = xm_xMaskFromGadgetID(inGadgetID) ; Retrieve the pointer to our xMask gadget. If *xMask : SetFocus_(*xMask\handleGadget) : EndIf ; Set focus to the xMask gadget if we found it. If SelectAll = #True ; Select all text in the xMask gadget LineLength = SendMessage_(*xMask\handleGadget, #WM_GETTEXTLENGTH, 0, 0) + 1 ; Get the length of the current line. SendMessage_(*xMask\handleGadget, #EM_SETSEL, 0, LineLength) ; And select the text. EndIf ; EndProcedure Procedure xmSelectAllOnFocus(inGadgetID.l, SelectOnFocus.b) ; #True to enable selecting all text on focus. ; This will set the option to select the whole xMask text when the gadget receives focus. Protected *xMask.s_xMask ; This will hold the information for our xMask gadget. *xMask = xm_xMaskFromGadgetID(inGadgetID) ; Retrieve the pointer to our xMask gadget. If *xMask : *xMask\SelectAllOnFocus = SelectOnFocus : EndIf ; Set the option if we found the xMask gadget id. EndProcedure Procedure.s xmGetGadgetText(inGadgetID.l) ; This is similar to GetGadgetText() and will return the text in the xMask gadget. Protected *xMask.s_xMask ; This will hold the information for our xMask gadget. Protected LineLength.l ; This will hold the length of the xMask text line. Protected holdString.s ; This will temporarily hold the text from the xMask gadget. *xMask = xm_xMaskFromGadgetID(inGadgetID) ; Retrieve the pointer to our xMask gadget. If *xMask ; Make sure we returned a valid xMask gadget. LineLength = SendMessage_(*xMask\handleGadget, #WM_GETTEXTLENGTH, 0, 0) + 1 ; Get the length of the current line. holdString = Space(LineLength) ; Empty our string with just spaces. SendMessage_(*xMask\handleGadget, #WM_GETTEXT, LineLength, @holdString) ; Now get the text for the current xMask gadget. If *xMask\Mask = "#Financial" holdString = xm_FinancialValue(*xMask, holdString) Else holdString = xm_ReturnText(*xMask, holdString) EndIf ; EndIf ; ProcedureReturn holdString ; EndProcedure Procedure xmSetGadgetText(inGadgetID.l, inText.s) ; InText is the text to set. Without the mask. ; This is similar to GetGadgetText() and will return the text in the xMask gadget. Protected *xMask.s_xMask ; This will hold the information for our xMask gadget. Protected holdString.s ; This will hold the merged string with the mask. *xMask = xm_xMaskFromGadgetID(inGadgetID) ; Retrieve the pointer to our xMask gadget. If *xMask ; Make sure we returned a valid xMask gadget. If *xMask\Mask = "#Financial" ; Special case financial mask. holdString = xm_FinancialMaskToText(inText, *xMask\FinancialShowSeparator, *xMask\FinancialAutoDecimal, *xMask\FinancialDecimalPlaces, *xMask\FinancialRoundTruncatedDecimals, *xMask\FinancialNegativeAsParan, #False) ; Merge the text with the mask. Else ; Normal mask. holdString = xm_MaskToText(*xMask\Mask, inText, *xMask\PlaceHolder, #False) ; Merge the text with the mask. EndIf ; If holdString <> "" ; Make sure our text merged successfully with the mask. If one of our characters ; does not fit the mask, it will return an empty string. SendMessage_(*xMask\handleGadget, #WM_SETTEXT, 0, holdString) ; Now get the text for the current xMask gadget. If *xMask\AddressChangeEvent ; If we've set a Change Event procedure, call it. If *xMask\Mask = "#Financial" CallFunctionFast(*xMask\AddressChangeEvent, *xMask\idGadget, xm_FinancialValue(*xMask, holdString)) ; Call our Change Event procedure. Else CallFunctionFast(*xMask\AddressChangeEvent, *xMask\idGadget, holdString) ; Call our Change Event procedure. EndIf ; EndIf ; EndIf ; EndIf ; EndProcedure Procedure xmSetMask(inGadgetID.l, InMask.s, inText.s, PlaceHolder.b) ; InMask is the new mask to use. InText is the text to set, without the mask. ; This will set a new Mask, Text and Placeholder for the xMask gadget. Protected *xMask.s_xMask ; This will hold the information for our xMask gadget. Protected holdString.s ; This will hold the merged string with the mask. *xMask = xm_xMaskFromGadgetID(inGadgetID) ; Retrieve the pointer to our xMask gadget. If *xMask ; Make sure we returned a valid xMask gadget. *xMask\Mask = InMask ; Set the new mask. *xMask\PlaceHolder = PlaceHolder ; Now set the new placeholder character. *xMask\MaxLength = xm_GetMaskMaxLength(*xMask\Mask) ; Retrieve the maximum for the mask. SendMessage_(*xMask\handleGadget, #EM_LIMITTEXT, *xMask\MaxLength, 0) ; Now set the maximum number of allowed characters. If *xMask\Mask = "#Financial" holdString = xm_FinancialMaskToText(inText, *xMask\FinancialShowSeparator, *xMask\FinancialAutoDecimal, *xMask\FinancialDecimalPlaces, *xMask\FinancialRoundTruncatedDecimals, *xMask\FinancialNegativeAsParan, #False) Else holdString = xm_MaskToText(*xMask\Mask, inText, *xMask\PlaceHolder, #False) ; Merge the text with the mask. If holdString = "" : holdString = xm_MaskToText(*xMask\Mask, inText, *xMask\PlaceHolder, #True) : EndIf ; If our new text fails the mask, show it with just the placeholder character. EndIf ; SendMessage_(*xMask\handleGadget, #WM_SETTEXT, 0, holdString) ; Now get the text for the current xMask gadget. If *xMask\AddressChangeEvent ; If we've set a Change Event procedure, call it. If *xMask\Mask = "#Financial" CallFunctionFast(*xMask\AddressChangeEvent, *xMask\idGadget, xm_FinancialValue(*xMask, holdString)) ; Call our Change Event procedure. Else CallFunctionFast(*xMask\AddressChangeEvent, *xMask\idGadget, holdString) ; Call our Change Event procedure. EndIf ; EndIf ; EndIf ; EndProcedure Procedure xmSetFinancialMaskOptions(inGadgetID.l, ShowThousandsSeparator.b, ForceDecimals.b, NumberDecimals.b, RoundDecimals.b, ShowNegativeAsParan.b) ; This will set the options for a special case financial mask. ; ShowThousandsSeparator will show the ',' if True. ; ForceDecimals will display decimals even if there are none for the field. "0" would become "0.00" ; NumberDecimals controls how many decimal places are allowed after the decimal point. ; RoundDecimals control whether we round off or truncate when there are too many decimals in the xMask gadget. ; ShowNegativeAsParan will show a negative number with parantheses rather than a '-' sign. Protected *xMask.s_xMask ; This will hold the information for our xMask gadget. Protected holdString.s ; *xMask = xm_xMaskFromGadgetID(inGadgetID) ; Retrieve the pointer to our xMask gadget. If *xMask ; Make sure we returned a valid xMask gadget. *xMask\FinancialShowSeparator = ShowThousandsSeparator ; *xMask\FinancialAutoDecimal = ForceDecimals ; *xMask\FinancialDecimalPlaces = NumberDecimals ; *xMask\FinancialNegativeAsParan = ShowNegativeAsParan ; *xMask\FinancialRoundTruncatedDecimals = RoundDecimals ; holdString = xm_FinancialValue(*xMask, xmGetGadgetText(inGadgetID)) ; holdString = xm_FinancialMaskToText(holdString, ShowThousandsSeparator, ForceDecimals, NumberDecimals, RoundDecimals, ShowNegativeAsParan, #False) ; EndIf EndProcedure Procedure xmForceGadgetText(inGadgetID.l, inText.s) ; InText is the text to set including the mask. ; This is similar to xmSetGadgetText() except it will enter the InText parameter exactly as is. ; Use this function at your own risk - if you know the text you're setting is correct with the ; mask created. Protected *xMask.s_xMask ; This will hold the information for our xMask gadget. *xMask = xm_xMaskFromGadgetID(inGadgetID) ; Retrieve the pointer to our xMask gadget. If *xMask ; Make sure we returned a valid xMask gadget. SendMessage_(*xMask\handleGadget, #WM_SETTEXT, 0, inText) ; Now get the text for the current xMask gadget. If *xMask\AddressChangeEvent ; If we've set a Change Event procedure, call it. CallFunctionFast(*xMask\AddressChangeEvent, *xMask\idGadget, inText) ; Call our Change Event procedure. EndIf ; EndIf ; EndProcedure Procedure xmDisableGadget(inGadgetID.l, Disable.b) ; #True to disable, #False to enable. Protected *xMask.s_xMask ; This will lock the xMask gadget and prevent data entry. *xMask = xm_xMaskFromGadgetID(inGadgetID) ; Retrieve the pointer to our xMask gadget. If *xMask ; Make sure we returned a valid xMask gadget. SendMessage_(*xMask\handleGadget, #EM_SETREADONLY, Disable, 0) ; Now get the text for the current xMask gadget. EndIf ; EndProcedure Procedure xmSetChangeEvent(inGadgetID.l, AddressOfChangeEventProcedure.l) ; This will set the procedure used for the xMask change event. When we make a change in our xMask gadget, we'll call that ; procedure with the new xMask value. It doesn't matter what you call the event procedure as long as it has a LONG ; as the first parameter and a STRING as the second. The first will contain the gadget id, the second the new text. Protected *xMask.s_xMask ; This will hold the information for our xMask gadget. *xMask = xm_xMaskFromGadgetID(inGadgetID) ; Retrieve the pointer to our xMask gadget. If *xMask ; Make sure we returned a valid xMask gadget. *xMask\AddressChangeEvent = AddressOfChangeEventProcedure ; Set the address. EndIf ; EndProcedure Procedure xmSetBadKeyEvent(inGadgetID.l, AddressOfBadKeyEventProcedure.l) ; See xmSetChangeEvent for a description. This will fire events if we type a key that conflicts with our mask. Protected *xMask.s_xMask ; This will hold the information for our xMask gadget. *xMask = xm_xMaskFromGadgetID(inGadgetID) ; Retrieve the pointer to our xMask gadget. If *xMask ; Make sure we returned a valid xMask gadget. *xMask\AddressBadKeyEvent = AddressOfBadKeyEventProcedure ; Set the address. EndIf ; EndProcedure Procedure xmSetLostFocusEvent(inGadgetID.l, AddressOfLostFocusEventProcedure.l) ; See xmSetChangeEvent for a description. This will fire events if our xMask gadget loses focus. Protected *xMask.s_xMask ; This will hold the information for our xMask gadget. *xMask = xm_xMaskFromGadgetID(inGadgetID) ; Retrieve the pointer to our xMask gadget. If *xMask ; Make sure we returned a valid xMask gadget. *xMask\AddressLostFocusEvent = AddressOfLostFocusEventProcedure ; Set the address. EndIf ; EndProcedure Procedure xmSetReturnEvent(inGadgetID.l, AddressOfReturnEventProcedure.l) ; See xmSetChangeEvent for a description. This will fire events if we press the return key in our xMask gadget. Protected *xMask.s_xMask ; This will hold the information for our xMask gadget. *xMask = xm_xMaskFromGadgetID(inGadgetID) ; Retrieve the pointer to our xMask gadget. If *xMask ; Make sure we returned a valid xMask gadget. *xMask\AddressReturnEvent = AddressOfReturnEventProcedure ; Set the address. EndIf ; EndProcedure ;- Public xMask Functions - Custom Functions Procedure xmDestroy(inGadgetID.l) ; This will remove the specified xMask gadget. Protected *xMask.s_xMask ; This will hold the information for our xMask gadget. *xMask = xm_xMaskFromGadgetID(inGadgetID) ; Retrieve the pointer to our xMask gadget. If *xMask ; Make sure we have a valid xMask structure. DestroyWindow_(*xMask\handleGadget) ; Now destroy the xMask gadget. ResetList(xmTrack()) ; Reset our xMask gadget list. While NextElement(xmTrack()) ; Cyle through our list, looking for the xMask gadget we're removing. If xmTrack()\idPointer = *xMask : DeleteElement(xmTrack()) : EndIf ; And remove it from the list. Wend ; FreeMemory(*xMask) ; And free the memory used by the xMask control. EndIf ; EndProcedure Procedure xmDestroyAll() ; This will remove all xMask gadgets. Protected *xMask.s_xMask ; This will hold the information for our xMask gadget. ResetList(xmTrack()) ; Reset our xMask gadget list. While NextElement(xmTrack()) ; Cyle through our list of xMask gadgets. *xMask = xmTrack()\idPointer ; Now point to the xMask gadget. DestroyWindow_(*xMask\handleGadget) ; Destroy the xMask gadget. DeleteElement(xmTrack()) ; And remove it from the list. FreeMemory(*xMask) ; And free the memory used by the xMask control. Wend ; EndProcedure ;- Custom xMask Gadget Callback Procedure Procedure xm_EditCallback(WindowID.l, Message.l, wparam.l, lParam.l) ; This is a custom callback function used by our xMask gadget. We'll use this to check ; for various events and handle them in the appropriate manner. Protected HoldSelStart.l Protected HoldSelEnd.l ; These two values will hold the start and end selection values for our edit control. Protected HoldCharacter.b ; This will be the return value for our xm_CheckAndReplace() function. A 0 indicates we ignore the ; character. Anything else means we replace the current character with the new character. Protected holdString.s ; This will hold the text in the xMask gadget. We'll change the character in this when we ; replace something and then set it back to the xMask gadget. Protected ReturnCallback.l ; This is the return value given by the CallWindowProc_() procedure. Protected *xMask.s_xMask ; This is the xMask structure used to store information on the xMask gadget receiving the event. Protected LineLength.l ; This will hold the length of the line in the xMask gadget. Protected HoldLoopEnd.l ; This will store the end selection for looping purposes. Protected HoldLoopStart.l ; This will store the start selection - mainly for resetting a selection after a cut operation. Protected HoldClipText.s ; This will store the text data on the clipboard for a paste event. Protected ClipboardPosition.l ; A position counter for reading the text on a clipboard while pasting. Protected CanPaste.b ; True if we can paste the text from the clipboard into the xMask gadget. False if not. *xMask = xm_xMaskFromHandle(WindowID) ; Retrieve our xMask pointer based on the xMask gadget handle. If #ES_READONLY & GetWindowLong_(*xMask\handleGadget, #GWL_STYLE) ; This will test to see if our xMask gadget is currently set to read-only status. ; It will return a non-zero value if it is read only. ReturnCallback = CallWindowProc_(*xMask\OldCallback, WindowID, Message, wparam, lParam) ; Now call the original callback function for the Edit control for any messages. ProcedureReturn ReturnCallback ; And exit without processing any custom callback procedures. EndIf ; If Message = #WM_CHAR ; A key was pressed. LineLength = SendMessage_(*xMask\handleGadget, #WM_GETTEXTLENGTH, 0, 0) + 1 ; Get the length of the current line. holdString = Space(LineLength) ; Empty our string with just spaces. SendMessage_(*xMask\handleGadget, #WM_GETTEXT, LineLength, @holdString) ; Now get the text for the current xMask gadget. SendMessage_(*xMask\handleGadget, #EM_GETSEL, @HoldSelStart, @HoldSelEnd) ; Retrieve the start and end selection values. We'll use this when replacing characters. If *xMask\Mask = "#Financial" ; Special case financial mask. If xm_FinancialCheckAndReplace(*xMask, holdString, wparam, HoldSelStart, HoldSelEnd) ; The character is okay. holdString = Mid(holdString, 1, HoldSelStart)+Chr(wparam)+Mid(holdString, HoldSelEnd + 1, LineLength) ; Rebuild the string with our new character. SendMessage_(*xMask\handleGadget, #WM_SETTEXT, 0, @holdString) ; Now insert the new text into the control. SendMessage_(*xMask\handleGadget, #EM_SETSEL, HoldSelStart + 1, HoldSelStart + 1) ; And set our text selection to the next character. If *xMask\AddressChangeEvent ; If we've set a Change Event procedure, call it. CallFunctionFast(*xMask\AddressChangeEvent, *xMask\idGadget, xm_FinancialValue(*xMask, holdString)) ; EndIf ; Else ; A bad character was passed. If *xMask\AddressBadKeyEvent And wparam >= 33 ; If we've set a Change Event procedure, call it. CallFunctionFast(*xMask\AddressBadKeyEvent, *xMask\idGadget, "#Financial", Chr(wparam), 0) ; Call our Change Event procedure with a bad typed key type error. EndIf ; EndIf ; Else ; Normal xMask gadget. If HoldSelStart <= *xMask\MaxLength ; Make sure we aren't typing past the end of mask. HoldCharacter = xm_CheckAndReplace(*xMask, wparam, @HoldSelStart, @HoldSelEnd, #False) ; Now check the key pressed against the current character in the mask. If HoldCharacter > 0 ; If this is greater than 0, the typed character passed our filter. Replace it with the current character. PokeB(@holdString + HoldSelStart, HoldCharacter) ; Since our procedure returns the ascii (byte) of the character we're going to replace with, Poke() the ; byte into the string containing the xMask text. SendMessage_(*xMask\handleGadget, #WM_SETTEXT, 0, @holdString) ; Now insert the new text into the control. SendMessage_(*xMask\handleGadget, #EM_SETSEL, HoldSelStart + 1, HoldSelStart + 1) ; And set our text selection to the next character. If *xMask\AddressChangeEvent ; If we've set a Change Event procedure, call it. CallFunctionFast(*xMask\AddressChangeEvent, *xMask\idGadget, xm_ReturnText(*xMask, holdString)) ; Call our Change Event procedure. EndIf ; Else ; The typed character was rejected... If *xMask\AddressBadKeyEvent And wparam >= 33 ; If we've set a Change Event procedure, call it. CallFunctionFast(*xMask\AddressBadKeyEvent, *xMask\idGadget, *xMask\Mask, Chr(wparam), 0) ; Call our Change Event procedure with a bad typed key type error. EndIf ; EndIf ; EndIf ; EndIf ; If *xMask\AddressReturnEvent And wparam = 13 ; If we've set a Return event and the user pressed the return key, call our event. CallFunctionFast(*xMask\AddressReturnEvent, *xMask\idGadget, xm_ReturnText(*xMask, holdString)) ; Call our Change Event procedure with a bad typed key type error. EndIf ; wparam = 0 ; Now flush the character from the message queue. ElseIf Message = #WM_SETFOCUS ; The xMask gadget has received the focus. If *xMask\Mask = "#Financial" ; Remove the parantheses from a negative number if that's how we're showing it. LineLength = SendMessage_(*xMask\handleGadget, #WM_GETTEXTLENGTH, 0, 0) + 1 ; Get the length of the current line. holdString = Space(LineLength) ; Empty our string with just spaces. SendMessage_(*xMask\handleGadget, #WM_GETTEXT, LineLength, @holdString) ; Now get the text for the current xMask gadget. SendMessage_(*xMask\handleGadget, #EM_GETSEL, @HoldSelStart, @HoldSelEnd) ; Retrieve the start and end selection values. We'll use this to return to the original selected position. holdString = xm_FinancialValue(*xMask, holdString) ; Return the plain value for our financial 'mask'. holdString = xm_FinancialMaskToText(holdString, *xMask\FinancialShowSeparator, *xMask\FinancialAutoDecimal, *xMask\FinancialDecimalPlaces, *xMask\FinancialRoundTruncatedDecimals, *xMask\FinancialNegativeAsParan, #True) ; Now recreate our mask based on the plain values. SendMessage_(*xMask\handleGadget, #WM_SETTEXT, 0, @holdString) ; Now insert the new text into the control. SendMessage_(*xMask\handleGadget, #EM_SETSEL, HoldSelStart, HoldSelStart) ; And set our text selection to the next character. EndIf ; If *xMask\SelectAllOnFocus = #True ; We've enabled the option to select all text on focus. LineLength = SendMessage_(*xMask\handleGadget, #WM_GETTEXTLENGTH, 0, 0) + 1 ; Get the length of the current line. SendMessage_(*xMask\handleGadget, #EM_SETSEL, 0, LineLength) ; EndIf ; ElseIf Message = #WM_KILLFOCUS ; The user has clicked on a different control. We lost focus. If *xMask\Mask = "#Financial" ; The xMask gadget is using a special case financial 'mask'. If *xMask\FinancialNegativeAsParan = #True ; If we were in the xMask gadget and this was true, we replaced the '(' & ')' with '-'. ; So now we just need to put it back. LineLength = SendMessage_(*xMask\handleGadget, #WM_GETTEXTLENGTH, 0, 0) + 1 ; Get the length of the current line. holdString = Space(LineLength) ; Empty our string with just spaces. SendMessage_(*xMask\handleGadget, #WM_GETTEXT, LineLength, @holdString) ; Now get the text for the current xMask gadget. holdString = xm_FinancialValue(*xMask, holdString) ; Return the plain value for our financial 'mask'. If *xMask\AddressLostFocusEvent ; If we've set a Change Event procedure, call it. CallFunctionFast(*xMask\AddressLostFocusEvent, *xMask\idGadget, holdString) ; Call our Change Event procedure with a lost focus type event. We'll pass back the ; unformatted value. EndIf ; holdString = xm_FinancialMaskToText(holdString, *xMask\FinancialShowSeparator, *xMask\FinancialAutoDecimal, *xMask\FinancialDecimalPlaces, *xMask\FinancialRoundTruncatedDecimals, *xMask\FinancialNegativeAsParan, #False) ; Now recreate our mask with the parantheses formatting. SendMessage_(*xMask\handleGadget, #WM_SETTEXT, 0, @holdString) ; Now insert the new text into the control. EndIf ; Else ; If *xMask\AddressLostFocusEvent ; If we've set a Change Event procedure, call it. LineLength = SendMessage_(*xMask\handleGadget, #WM_GETTEXTLENGTH, 0, 0) + 1 ; Get the length of the current line. holdString = Space(LineLength) ; Empty our string with just spaces. SendMessage_(*xMask\handleGadget, #WM_GETTEXT, LineLength, @holdString) ; Now get the text for the current xMask gadget. CallFunctionFast(*xMask\AddressLostFocusEvent, *xMask\idGadget, xm_ReturnText(*xMask, holdString)) ; Call our Change Event procedure with a lost focus type event. EndIf ; EndIf ; ElseIf Message = #WM_KEYDOWN ; A key is down. This event will fire along with #WM_CHAR. wParam returns the virtual key code. If wparam = #VK_DELETE ; User pressed the Delete key. If HoldSelStart < *xMask\MaxLength ; Make sure we aren't typing past the end of mask. LineLength = SendMessage_(*xMask\handleGadget, #WM_GETTEXTLENGTH, 0, 0) + 1 ; Get the length of the current line. holdString = Space(LineLength) ; Empty our string with just spaces. SendMessage_(*xMask\handleGadget, #WM_GETTEXT, LineLength, @holdString) ; Now get the text for the current xMask gadget. SendMessage_(*xMask\handleGadget, #EM_GETSEL, @HoldSelStart, @HoldSelEnd) ; Retrieve the start and end selection values. We'll use this when replacing characters. HoldLoopStart = HoldSelStart ; Store the start selection temporarily. HoldLoopEnd = HoldSelEnd ; Store the end selection temporarily. Repeat ; We'll loop to the end of HoldLoopEnd rather than HoldSelEnd because HoldSelEnd ; can be changed by xm_CheckAndReplace. If *xMask\Mask = "#Financial" ; Special case financial mask. If HoldSelEnd > HoldSelStart ; We've selected one or more characters. holdString = Mid(holdString, 1, HoldSelStart)+Mid(holdString, HoldSelEnd + 1, LineLength) ; Delete the selected text from our xMask gadget. Do not rebuild the mask. We'll ; worry about that when the gadget loses focus. Else ; No selection. Delete the character to the right of the cursor. holdString = Mid(holdString, 1, HoldSelStart)+Mid(holdString, HoldSelEnd + 2, LineLength) ; Delete the selected text from our xMask gadget. Do not rebuild the mask. We'll ; worry about that when the gadget loses focus. EndIf ; Else ; Normal xMask gadget mask. HoldCharacter = xm_CheckAndReplace(*xMask, *xMask\PlaceHolder, @HoldSelStart, @HoldSelEnd, #True) ; Now check the key pressed against the current character in the mask. If HoldCharacter > 0 ; If this is greater than 0, the typed character passed our filter. Replace it with the current character. PokeB(@holdString + HoldSelStart, HoldCharacter) ; Since our procedure returns the ascii (byte) of the character we're going to replace with, Poke() the ; byte into the string containing the xMask text. HoldSelStart + 1 ; Now increment our start selection to test the next character. EndIf ; EndIf ; Until HoldSelEnd >= HoldLoopEnd Or HoldSelStart = HoldLoopEnd ; Loop until the end of the selection. If HoldSelStart is equal to HoldLoopEnd ; we're trying to delete the character outside of a selection. SendMessage_(*xMask\handleGadget, #WM_SETTEXT, 0, @holdString) ; Now insert the new text into the control. If *xMask\Mask = "#Financial" ; Special case financial mask. SendMessage_(*xMask\handleGadget, #EM_SETSEL, HoldSelStart, HoldSelStart) ; Reselect the original selection. Else ; Normal mask. SendMessage_(*xMask\handleGadget, #EM_SETSEL, HoldLoopStart, HoldLoopEnd) ; Reselect the original selection. EndIf ; wparam = 0 ; And flush the delete message. If *xMask\AddressChangeEvent ; If we've set a Change Event procedure, call it. If *xMask\Mask = "#Financial" ; Special case financial mask. CallFunctionFast(*xMask\AddressChangeEvent, *xMask\idGadget, xm_FinancialValue(*xMask, holdString)) Else CallFunctionFast(*xMask\AddressChangeEvent, *xMask\idGadget, xm_ReturnText(*xMask, holdString)) ; Call our Change Event procedure. EndIf ; EndIf ; Else ; Trying to type a character after the mask. wparam = 0 ; ...so cancel the key. EndIf ; ElseIf wparam = #VK_BACK ; User hit the backspace key. LineLength = SendMessage_(*xMask\handleGadget, #WM_GETTEXTLENGTH, 0, 0) + 1 ; Get the length of the current line. holdString = Space(LineLength) ; Empty our string with just spaces. SendMessage_(*xMask\handleGadget, #WM_GETTEXT, LineLength, @holdString) ; Now get the text for the current xMask gadget. SendMessage_(*xMask\handleGadget, #EM_GETSEL, @HoldSelStart, @HoldSelEnd) ; Retrieve the start and end selection values. We'll use this when replacing characters. If HoldSelStart = 0 ; We are at the very beginning of our xMask gadget. wparam = 0 ; Cancel the backspace key. Else ; Not at the beginning of the xMask gadget. If HoldSelEnd - HoldSelStart = 0 : HoldSelEnd - 1 : HoldSelStart - 1 : EndIf ; HoldLoopStart = HoldSelStart ; Store the start selection temporarily. HoldLoopEnd = HoldSelEnd ; Store the end selection temporarily. Repeat ; We'll loop to the end of HoldLoopEnd rather than HoldSelEnd because HoldSelEnd ; can be changed by xm_CheckAndReplace. If *xMask\Mask = "#Financial" ; Special case financial mask. holdString = Mid(holdString, 1, HoldSelStart)+Mid(holdString, HoldSelEnd + 2, LineLength) ; Delete the selected text from our xMask gadget. Do not rebuild the mask. We'll ; worry about that when the gadget loses focus. Else ; Normal xMask mask. HoldCharacter = xm_CheckAndReplace(*xMask, *xMask\PlaceHolder, @HoldSelStart, @HoldSelEnd, #True) ; Now check the key pressed against the current character in the mask. If HoldCharacter > 0 ; If this is greater than 0, the typed character passed our filter. Replace it with the current character. PokeB(@holdString + HoldSelStart, HoldCharacter) ; Since our procedure returns the ascii (byte) of the character we're going to replace with, Poke() the ; byte into the string containing the xMask text. HoldSelStart + 1 ; Now increment our start selection to test the next character. EndIf ; EndIf ; Until HoldSelEnd >= HoldLoopEnd Or HoldSelStart = HoldLoopEnd ; Loop until the end of the selection. If HoldSelStart is equal to HoldLoopEnd ; we're trying to delete the character outside of a selection. SendMessage_(*xMask\handleGadget, #WM_SETTEXT, 0, @holdString) ; Now insert the new text into the control. SendMessage_(*xMask\handleGadget, #EM_SETSEL, HoldLoopStart, HoldLoopEnd) ; Reselect the original selection. wparam = 0 ; And flush the delete message. If *xMask\AddressChangeEvent ; If we've set a Change Event procedure, call it. If *xMask\Mask = "#Financial" ; Special case financial mask. CallFunctionFast(*xMask\AddressChangeEvent, *xMask\idGadget, xm_FinancialValue(*xMask, holdString)) Else CallFunctionFast(*xMask\AddressChangeEvent, *xMask\idGadget, xm_ReturnText(*xMask, holdString)) ; Call our Change Event procedure. EndIf ; EndIf ; EndIf ; EndIf ; ElseIf Message = #WM_CLEAR ; The user selected the Delete menu option (ie, from the right-click menu on an Edit control). If HoldSelStart < *xMask\MaxLength ; Make sure we aren't typing past the end of mask. LineLength = SendMessage_(*xMask\handleGadget, #WM_GETTEXTLENGTH, 0, 0) + 1 ; Get the length of the current line. holdString = Space(LineLength) ; Empty our string with just spaces. SendMessage_(*xMask\handleGadget, #WM_GETTEXT, LineLength, @holdString) ; Now get the text for the current xMask gadget. SendMessage_(*xMask\handleGadget, #EM_GETSEL, @HoldSelStart, @HoldSelEnd) ; Retrieve the start and end selection values. We'll use this when replacing characters. HoldLoopStart = HoldSelStart ; Store the start selection temporarily. HoldLoopEnd = HoldSelEnd ; Store the end selection temporarily. Repeat ; We'll loop to the end of HoldLoopEnd rather than HoldSelEnd because HoldSelEnd ; can be changed by xm_CheckAndReplace. If *xMask\Mask = "#Financial" ; Special case financial mask. holdString = Mid(holdString, 1, HoldSelStart)+Mid(holdString, HoldSelEnd + 1, LineLength) ; Delete the selected text from our xMask gadget. Do not rebuild the mask. We'll ; worry about that when the gadget loses focus. Else ; Normal mask. HoldCharacter = xm_CheckAndReplace(*xMask, *xMask\PlaceHolder, @HoldSelStart, @HoldSelEnd, #True) ; Now check the key pressed against the current character in the mask. If HoldCharacter > 0 ; If this is greater than 0, the typed character passed our filter. Replace it with the current character. PokeB(@holdString + HoldSelStart, HoldCharacter) ; Since our procedure returns the ascii (byte) of the character we're going to replace with, Poke() the ; byte into the string containing the xMask text. HoldSelStart + 1 ; Now increment our start selection to test the next character. EndIf ; EndIf ; Until HoldSelEnd >= HoldLoopEnd Or HoldSelStart = HoldLoopEnd ; Loop until the end of the selection. If HoldSelStart is equal to HoldLoopEnd ; we're trying to delete the character outside of a selection. SendMessage_(*xMask\handleGadget, #WM_SETTEXT, 0, @holdString) ; Now insert the new text into the control. If *xMask\Mask = "#Financial" ; Special case financial mask. SendMessage_(*xMask\handleGadget, #EM_SETSEL, HoldSelStart, HoldSelStart) ; Reselect the original selection. Else ; Normal mask. SendMessage_(*xMask\handleGadget, #EM_SETSEL, HoldLoopStart, HoldLoopEnd) ; Reselect the original selection. EndIf Message = 0 ; And flush the delete message. If *xMask\AddressChangeEvent ; If we've set a Change Event procedure, call it. CallFunctionFast(*xMask\AddressChangeEvent, *xMask\idGadget, xm_ReturnText(*xMask, holdString)) ; Call our Change Event procedure. EndIf ; Else ; Trying to type a character after the mask. Message = 0 ; ...so cancel the key. EndIf ; ElseIf Message = #WM_COPY ; User copying text. LineLength = SendMessage_(*xMask\handleGadget, #WM_GETTEXTLENGTH, 0, 0) + 1 ; Get the length of the current line. holdString = Space(LineLength) ; Empty our string with just spaces. SendMessage_(*xMask\handleGadget, #WM_GETTEXT, LineLength, @holdString) ; Now get the text for the current xMask gadget. SendMessage_(*xMask\handleGadget, #EM_GETSEL, @HoldSelStart, @HoldSelEnd) ; Retrieve the start and end selection values. We'll use this when replacing characters. ClearClipboard() ; Clear the clipboard HoldClipText = PeekS(@holdString + HoldSelStart, HoldSelEnd - HoldSelStart) ; Now copy the selected text to our string. ; SetClipboardText(HoldClipText) ; And set the clipboard with the text. Message = 0 ; And flush the message queue. ElseIf Message = #WM_CUT ; User trying to cut text from the string gadget. Return a 0 Message to cancel. LineLength = SendMessage_(*xMask\handleGadget, #WM_GETTEXTLENGTH, 0, 0) + 1 ; Get the length of the current line. holdString = Space(LineLength) ; Empty our string with just spaces. SendMessage_(*xMask\handleGadget, #WM_GETTEXT, LineLength, @holdString) ; Now get the text for the current xMask gadget. SendMessage_(*xMask\handleGadget, #EM_GETSEL, @HoldSelStart, @HoldSelEnd) ; Retrieve the start and end selection values. We'll use this when replacing characters. ClearClipboard() ; Clear the clipboard of it's current contents SetClipboardText(PeekS(@holdString + HoldSelStart, HoldSelEnd - HoldSelStart)) ; Now copy the current selection to the clipboard HoldLoopStart = HoldSelStart ; Store the start selection temporarily. HoldLoopEnd = HoldSelEnd ; Store the end selection temporarily. Repeat ; We'll loop to the end of HoldLoopEnd rather than HoldSelEnd because HoldSelEnd ; can be changed by xm_CheckAndReplace. If *xMask\Mask = "#Financial" ; Special case financial mask. holdString = Mid(holdString, 1, HoldSelStart)+Mid(holdString, HoldSelEnd + 1, LineLength) ; Delete the selected text from our xMask gadget. Do not rebuild the mask. We'll ; worry about that when the gadget loses focus. Else ; Normal mask. HoldCharacter = xm_CheckAndReplace(*xMask, *xMask\PlaceHolder, @HoldSelStart, @HoldSelEnd, #True) ; Now check the key pressed against the current character in the mask. If HoldCharacter > 0 ; If this is greater than 0, the typed character passed our filter. Replace it with the current character. PokeB(@holdString + HoldSelStart, HoldCharacter) ; Since our procedure returns the ascii (byte) of the character we're going to replace with, Poke() the ; byte into the string containing the xMask text. HoldSelStart + 1 ; Now increment our start selection to test the next character. EndIf ; EndIf ; Until HoldSelEnd >= HoldLoopEnd Or HoldSelStart = HoldLoopEnd ; SendMessage_(*xMask\handleGadget, #WM_SETTEXT, 0, @holdString) ; Now insert the new text into the control. SendMessage_(*xMask\handleGadget, #EM_SETSEL, HoldLoopStart, HoldLoopEnd) ; Reselect the original selection. If *xMask\AddressChangeEvent ; If we've set a Change Event procedure, call it. CallFunctionFast(*xMask\AddressChangeEvent, *xMask\idGadget, xm_ReturnText(*xMask, holdString)) ; Call our Change Event procedure. EndIf ; Message = 0 ; Now clear the Cut message. ElseIf Message = #WM_PASTE ; User trying to paste text or otherwise into the xMask gadget. Return a 0 Message to cancel. ; HoldClipText = GetClipboardText() ; Store the clipboard text. If HoldClipText <> "" ; If the type of data on the clipboard is not text this will be empty. LineLength = SendMessage_(*xMask\handleGadget, #WM_GETTEXTLENGTH, 0, 0) + 1 ; Get the length of the current line. holdString = Space(LineLength) ; Empty our string with just spaces. SendMessage_(*xMask\handleGadget, #WM_GETTEXT, LineLength, @holdString) ; Now get the text for the current xMask gadget. SendMessage_(*xMask\handleGadget, #EM_GETSEL, @HoldSelStart, @HoldSelEnd) ; Retrieve the start and end selection values. We'll use this when replacing characters. HoldLoopStart = HoldSelStart ; Store the start selection temporarily. HoldLoopEnd = HoldSelEnd ; Store the end selection temporarily. ClipboardPosition = 0 ; Start our counter at 0 for string manipulation. This will point to the first ; character in the string (via a Peek() call). CanPaste = #True ; By default, set this to be True. If we find bad data, we'll set it false. If *xMask\Mask = "#Financial" ; Special case financial mask. If HoldSelStart <> HoldSelEnd ; We're trying to paste over the selected text. Clear the selected text first. holdString = Mid(holdString, 1, HoldSelStart)+Mid(holdString, HoldSelEnd + 1, LineLength) ; Delete the selected text from our xMask gadget. Do not rebuild the mask. We'll ; worry about that when the gadget loses focus. SendMessage_(*xMask\handleGadget, #EM_SETSEL, HoldSelStart, HoldSelStart) ; Set the cursor to the start of our original selection. HoldSelEnd = HoldSelStart HoldLoopEnd = HoldSelEnd ; EndIf ; Repeat ; HoldCharacter = PeekB(@HoldClipText + ClipboardPosition) ; If xm_FinancialCheckAndReplace(*xMask, holdString, HoldCharacter, HoldSelStart, HoldSelStart) = 1 ; Character is okay. holdString = Mid(holdString, 1, HoldSelStart)+Chr(HoldCharacter)+Mid(holdString, HoldSelEnd + 1, LineLength) ; Rebuild the string with our new character. HoldSelStart + 1 ; Increment our start selection to the next character. HoldSelEnd = HoldSelStart ; Else ; Bad character. CanPaste = #False ; Let the procedure know we can't paste. Break ; And break our of our loop. EndIf ; ClipboardPosition + 1 ; Now increment the counter for our clipboard text. This should point to the next ; character in our clipboard text string. Until ClipboardPosition = Len(HoldClipText) ; Loop to the end of either the clipboard text or the end of the mask. Else Repeat ; Normal mask. HoldCharacter = xm_CheckAndReplace(*xMask, PeekB(@HoldClipText + ClipboardPosition), @HoldSelStart, @HoldSelEnd, #False) ; Now check the key pressed against the current character in the mask. If HoldCharacter > 0 ; Check to see the returned character is valid. PokeB(@holdString + HoldSelStart, HoldCharacter) ; Since our procedure returns the ascii (byte) of the character we're going to replace with, Poke() the ; byte into the string containing the xMask text. HoldSelStart + 1 ; Increment our start selection to the next character. Else ; We tried pasting a bad character. The text on the clipboard will not fit the ; mask so we need to cancel the pasting. CanPaste = #False ; Let the procedure know we can't paste. Break ; And break our of our loop. EndIf ; ClipboardPosition + 1 ; Now increment the counter for our clipboard text. This should point to the next ; character in our clipboard text string. Until HoldSelEnd >= HoldLoopEnd Or ClipboardPosition = Len(HoldClipText) ; Loop to the end of either the clipboard text or the end of the mask. EndIf ; If (ClipboardPosition <> Len(HoldClipText) And HoldSelEnd = HoldLoopEnd) : CanPaste = #False : EndIf ; The clipboard text is larger than the selection size. If CanPaste = #True ; Check to see if we're allowed to paste the text. SendMessage_(*xMask\handleGadget, #WM_SETTEXT, 0, @holdString) ; Now insert the new text into the control. If *xMask\AddressChangeEvent ; If we've set a Change Event procedure, call it. If *xMask\Mask = "#Financial" ; Special case financial mask. CallFunctionFast(*xMask\AddressChangeEvent, *xMask\idGadget, holdString) ; Call our Change Event procedure. Else ; Normal mask. CallFunctionFast(*xMask\AddressChangeEvent, *xMask\idGadget, xm_ReturnText(*xMask, holdString)) ; Call our Change Event procedure. EndIf ; EndIf ; Else ; There was an error while trying to paste the text. If *xMask\AddressBadKeyEvent ; If we've set a Change Event procedure, call it. CallFunctionFast(*xMask\AddressBadKeyEvent, *xMask\idGadget, *xMask\Mask, HoldClipText, 1) ; Call our Change Event procedure with a bad typed key type error. EndIf ; EndIf ; SendMessage_(*xMask\handleGadget, #EM_SETSEL, HoldLoopStart, HoldLoopEnd) ; Reselect the original selection. EndIf ; Message = 0 ; Now flush the paste message. ElseIf Message = #WM_KEYUP ; ;Debug Str(wparam)+" : "+Str(lParam)+" : "+Str(GetAsyncKeyState_(#VK_CONTROL))+", "+Str(GetAsyncKeyState_(#VK_V)) If *xMask\AddressReturnEvent And wparam = 13 ; If we've set a Return event and the user pressed the return key, call our event. LineLength = SendMessage_(*xMask\handleGadget, #WM_GETTEXTLENGTH, 0, 0) + 1 ; Get the length of the current line. holdString = Space(LineLength) ; Empty our string with just spaces. SendMessage_(*xMask\handleGadget, #WM_GETTEXT, LineLength, @holdString) ; Now get the text for the current xMask gadget. If *xMask\Mask = "#Financial" ; Special case financial mask. CallFunctionFast(*xMask\AddressReturnEvent, *xMask\idGadget, holdString) ; Call our Change Event procedure with a bad typed key type error. Else ; CallFunctionFast(*xMask\AddressReturnEvent, *xMask\idGadget, xm_ReturnText(*xMask, holdString)) ; Call our Change Event procedure with a bad typed key type error. EndIf ; EndIf ; EndIf ; ReturnCallback = CallWindowProc_(*xMask\OldCallback, WindowID, Message, wparam, lParam) ; Now call the original callback function for the Edit control for any messages. ProcedureReturn ReturnCallback ; EndProcedure ;- xMask Gadget Creation Procedure Procedure.l xmCreate(inGadgetID.l, inParentID.l, x.l, y.l, Width.l, Height.l, Mask.s, Text.s, PlaceHolder.b) ; inGadgetID is the value used to create a normal TextGadget. inParentID is the hwnd of the parent (either WindowID() or ; GadgetID(). Mask is the actual mask we want to use for filtering and displaying the text. ; Text is the initial values used for the textbox, minus the mask. Placeholder will be a single character used as ; display when no text is entered. For example, an underline ("_") character. Protected *xMask.s_xMask ; This is the structure that will hold the new xMas gadget. Protected HoldText.s ; This will hold the new text for the xMask gadget. Protected xMaskStyle.l ; This will hold the style of the xMask gadget. *xMask = AllocateMemory(SizeOf(s_xMask)) ; Allocate the memory needed for our xMask structure. This structure will hold the settings for the xMask gadget. *xMask\idGadget = inGadgetID ; Store the actual gadget id. *xMask\handleParent = inParentID ; Store the handle to the parent window. Use this for event checking later. If Mask = "#Financial" ; We'll be using a special case mask - a financial mask. This will fill in from the right. xMaskStyle = #WS_CHILD | #WS_VISIBLE | #ES_RIGHT | #WS_TABSTOP Else xMaskStyle = #WS_CHILD | #WS_VISIBLE | #ES_LEFT | #WS_TABSTOP EndIf ; *xMask\handleGadget = CreateWindowEx_(#WS_EX_CONTROLPARENT | #WS_EX_CLIENTEDGE, "EDIT", 0, xMaskStyle, x, y, Width, Height, inParentID, inGadgetID, GetModuleHandle_(0), 0) ; Create the xMask gadget and store the handle of the gadget in our xMask structure. SendMessage_(*xMask\handleGadget, #WM_SETFONT, GetStockObject_(#DEFAULT_GUI_FONT), #True) ; Set our font to use the default GUI font. Otherwise, it'll use a really ugly system font. *xMask\x = x : *xMask\y = y : *xMask\Width = Width : *xMask\Height = Height ; Store the coordinates of the control. *xMask\Mask = Mask ; Now store our mask. *xMask\PlaceHolder = PlaceHolder ; Now store the placeholder character. This is the ascii equivalent of the character. *xMask\MaxLength = xm_GetMaskMaxLength(*xMask\Mask) ; Retrieve the maximum for the mask. *xMask\SelectAllOnFocus = #False ; By default, do not select the whole line when the xMask gadget receives focus. *xMask\FinancialShowSeparator = #True ; By default, in a Financial mask, we want to show the thousands separator automatically. *xMask\FinancialAutoDecimal = #True ; By default, in a Financial mask, we want to automatically show the decimal values. *xMask\FinancialDecimalPlaces = 2 ; By default, in a Financial mask, we want to show two decimal places. *xMask\FinancialNegativeAsParan = #True ; By default show '-100' as '(100)'. *xMask\FinancialRoundTruncatedDecimals = #False ; If our text contains more decimal places than allowed, round to the number of decimals. *xMask\AddressChangeEvent = 0 ; No Change Event by default. *xMask\AddressBadKeyEvent = 0 ; No Bad Key Event by default. *xMask\AddressLostFocusEvent = 0 ; No Lost Focus Event by default. *xMask\AddressReturnEvent = 0 ; No Return Key event by default. SendMessage_(*xMask\handleGadget, #EM_LIMITTEXT, *xMask\MaxLength, 0) ; Now set the maximum number of allowed characters. If Mask = "#Financial" ; We're using a special case Financial mask. HoldText = xm_FinancialMaskToText(Text, *xMask\FinancialShowSeparator, *xMask\FinancialAutoDecimal, *xMask\FinancialDecimalPlaces, *xMask\FinancialRoundTruncatedDecimals, *xMask\FinancialNegativeAsParan, #False) ; Now convert our mask and text into a proper text for display. Else HoldText = xm_MaskToText(Mask, Text, PlaceHolder, #False) ; Now convert our mask and text into a proper text for display. If HoldText = "" : HoldText = xm_MaskToText(Mask, "", PlaceHolder, #True) : EndIf ; The text we passed as a parameter does not fit our mask. EndIf ; SendMessage_(*xMask\handleGadget, #WM_SETTEXT, 0, @HoldText) ; And insert the text into the gadget. AddElement(xmTrack()) ; Add a new item to our xMask gadget linked list. This will keep track of all used xMask gadgets. xmTrack()\idPointer = *xMask xmTrack()\idGadget = inGadgetID xmTrack()\handleGadget = *xMask\handleGadget ; And store the handle of the xMask gadget as well as the pointer to our xMask structure. When ; we need to reference an individual xMask gadget in the callback funciton, we'll test against ; the idPointer variable. Store the gadget id to allow the user to reference functions by the ; PB gadget id rather than the xMask gadget handle. *xMask\OldCallback = GetWindowLong_(*xMask\handleGadget, #GWL_WNDPROC) ; Store the original callback procedure address for the Edit control. SetWindowLong_(*xMask\handleGadget, #GWL_WNDPROC, @xm_EditCallback()) ; Now set the custom callback to use for the new xMask gadget. ProcedureReturn *xMask ; Now return the pointer to the newly created xMask gadget. EndProcedure ; jaPBe Version=2.5.4.22 ; FoldLines=0000001400160047004800500055007F016201CB01CD0320032103BA03BB0416 ; FoldLines=041705D805D905E305E405EE05F005F905FA060B060C061E061F062706280644 ; FoldLines=06BA06CE06CF06DA06DB06E906EA06F606F7070307040710071207280729073B ; Build=0 ; FirstLine=1013 ; CursorPosition=2560 ; ExecutableFormat=Windows ; DontSaveDeclare ; EOF