xMask - A Masked Edit Gadget

Developed or developing a new product in PureBasic? Tell the world about it.
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

Hi Xombie,

I'm running this under PB_4 (didn't take much, about 6 changes, mostly to the test prog - and a Global in front of the list).

I also get bleeps per character entered, and can't figure out why, yet. (PB4 and XP Pro if you're interested).


However, that aside, this is very nice indeed! :D
@}--`--,-- A rose by any other name ..
Xombie
Addict
Addict
Posts: 898
Joined: Thu Jul 01, 2004 2:51 am
Location: Tacoma, WA
Contact:

Post by Xombie »

@Dare2 - could you try this version?

http://www.seijin.net/Storage/Code/xMask/xMask-PB4.pb

I'll work on the PB3.94 if you still need it, Straker.
Straker
Enthusiast
Enthusiast
Posts: 701
Joined: Wed Apr 13, 2005 10:45 pm
Location: Idaho, USA

Post by Straker »

Xombie wrote:I'll work on the PB3.94 if you still need it, Straker.
Unless its a quick fix and you can tell me which lines in the code need to be changed, don't worry about it, its not critical.

I'll be upgrading to PB4 in a couple of months, so I will use your new version then.
Image Image
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

Hi Xombie,

Still beeps and got a crash on first quick test, but I modded the test program a bit for things like the RightToLeft boolean param and so may have stuffed something up myself. Any bug I find is most probably my own, I've discovered. :)

Will give you more details later, when I have some time to do things a bit more thoroughly.

Neat, mate. I have learned a bit looking at your code.
@}--`--,-- A rose by any other name ..
Xombie
Addict
Addict
Posts: 898
Joined: Thu Jul 01, 2004 2:51 am
Location: Tacoma, WA
Contact:

Post by Xombie »

That's pretty old code. I think if I redid the whole thing I could probably get it down to half it's size at least and speed it up quite a bit. I hope you didn't learn any bad programming practices from it :)

Earlier when you got the beeping - did it still work? As in it worked but beeped every time you hit a key? I've tried my old code and new code and don't get any beeps. I'm *pretty* sure I hooked up the internal speaker. Oh. Is it a soundcard/Windows beep or an internal speaker beep?
Straker
Enthusiast
Enthusiast
Posts: 701
Joined: Wed Apr 13, 2005 10:45 pm
Location: Idaho, USA

Post by Straker »

Xombie wrote:Earlier when you got the beeping - did it still work? As in it worked but beeped every time you hit a key? I've tried my old code and new code and don't get any beeps. I'm *pretty* sure I hooked up the internal speaker. Oh. Is it a soundcard/Windows beep or an internal speaker beep?
Yes - it works fine with the beeping. Yes, everytime a key is pressed. My system sends internal speaker thru the soundcard so hard to say, but sounds like the internal speaker.
Image Image
Xombie
Addict
Addict
Posts: 898
Joined: Thu Jul 01, 2004 2:51 am
Location: Tacoma, WA
Contact:

Post by Xombie »

Well, I completely reworked this code. Now it's no longer it's own control. To use it you'd simply call ...

Code: Select all

If OpenWindow(0, 0, 0, 400, 300, "Test", #PB_Window_TitleBar | #PB_Window_SystemMenu)
   ;
   If CreateGadgetList(WindowID(0))
      ;
      StringGadget(0, 10, 10, 120, 20, "")
      ;
      SetGadgetMask(0, "999-99-9999", "123456789", '*', #False)
      ;
   EndIf
   ;
EndIf
It's just the SetGadgetMask() call that handles everything. It piggybacks off of an existing string gadget so you can call all resizes & etc... like normal for it. There is another procedure you can use to return the data without the mask - xm_GetRawData().

Let me know if it doesn't work! :D

http://www.seijin.net/Storage/Code/xMask/xMask.pb
Last edited by Xombie on Thu Aug 10, 2006 1:06 am, edited 1 time in total.
Straker
Enthusiast
Enthusiast
Posts: 701
Joined: Wed Apr 13, 2005 10:45 pm
Location: Idaho, USA

Post by Straker »

Love it!

Thanks Xombie.
Image Image
TerryHough
Enthusiast
Enthusiast
Posts: 781
Joined: Fri Apr 25, 2003 6:51 pm
Location: NC, USA
Contact:

Post by TerryHough »

Hmm...

Code: Select all

   If CreateGadgetList(WindowID(0))
      StringGadget(0, 10, 10, 120, 20, "")
      SetGadgetMask(0, "999-99-9999", "123456789", '*', #False)
   EndIf
Allows entry of any character including alphabetic, although the entry is formatted to match the mask.

Code: Select all

   If CreateGadgetList(WindowID(0))
      StringGadget(0, 10, 10, 120, 20, "", #PB_String_Numeric)
      SetGadgetMask(0, "999-99-9999", "123456789", '*', #False)
   EndIf
Even the numeric flag is ignored and you can still enter alpha and other characters.
:cry:
ebs
Enthusiast
Enthusiast
Posts: 557
Joined: Fri Apr 25, 2003 11:08 pm

Post by ebs »

Terry and Xombie,

I think I've found the problem. The FailedMask variable is getting cleared when it shouldn't be.
In the Procedure _xm_ReturnCharacter(), replace this line:

Code: Select all

If IsStatic And UsingPlaceholder : FailedMask = #True : Else : FailedMask = #False : EndIf
with:

Code: Select all

If UsingPlaceholder
   FailedMask = IsStatic
EndIf
and the mask works correctly.

Regards,
Eric

P.S. This is a GREAT piece of code!!
User avatar
Rook Zimbabwe
Addict
Addict
Posts: 4322
Joined: Tue Jan 02, 2007 8:16 pm
Location: Cypress TX
Contact:

Small prob

Post by Rook Zimbabwe »

Hi,
I try to run the test program in PB4 on Windows XP Pro...
I get an ERROR in line 1500 od xMask.pb that says: xmTrack() is not a function array macro or linked list...

both programs are saved to the same directory and open. Did I miss a userlib? :?:
Xombie
Addict
Addict
Posts: 898
Joined: Thu Jul 01, 2004 2:51 am
Location: Tacoma, WA
Contact:

Post by Xombie »

I hate to post the full code rather than a link to a PB file but I don't have access to my ftp server at the moment. I think you downloaded the wrong one from above. Here it is just in case.

Code: Select all

;-
; -   Handle special case "" set that clears the current text.
; -   Do I need the 'If HoldSelect\X <= __xm_Main()\MaxLength' line in the callback?  Won't the limit handle it automatically.
;-
EnableExplicit
;-
Enumeration ; Special Case Formatting
   #XM_Format_Currency
EndEnumeration
;-
Structure Char
   c.c
EndStructure
Structure __s_xMask_Main
   ;
   Gadget.l
   ;
   Handle.l
   ;
   TextMask.s
   ;
   Text.s
   ;
   PlaceHolder.c
   ;
   RightToLeft.b
   ;
   OldCallback.l
   ;
   MaxLength.l
   ;
   SelectAllOnFocus.b
   ; If True, when the the xMask gadget is selected it will select the whole line.
   AdjustingText.b
   ; True if the callback is temporarily setting text.
EndStructure
;-
Global NewList __xm_Main.__s_xMask_Main()
;-
Procedure InsertCharacter(*HoldText.l, Index.l, Character.c)
   ;
   CompilerIf #PB_Compiler_Unicode = #True
      ;
      PokeC(*HoldText + (Index << 1), Character)
      ;
   CompilerElse
      ;
      PokeC(*HoldText + Index, Character)
      ;
   CompilerEndIf 
   ;
EndProcedure
;-
Procedure.l _xm_GetMaxLength(TextMask.s)
   ;
   Define.Char *HoldChar
   ;
   Define.b AllowAsNonFormatting
   ;
   Define.l lResult
   ;
   *HoldChar = @TextMask
   ;
   While *HoldChar\c <> 0
      ;
      If AllowAsNonFormatting
         ;
         lResult + 1
         ;
         AllowAsNonFormatting = #False
         ;
      Else
         ;
         If *HoldChar\c = '0'
            ; This is a required number field.
            lResult + 1
            ;
         ElseIf *HoldChar\c = '9'
            ; This is a non-required number field.  Either a digit or a space is allowed here.
            lResult + 1
            ;
         ElseIf *HoldChar\c = 'L'
            ; A - Z, entry required
            lResult + 1
            ;
         ElseIf *HoldChar\c = '?'
            ; A - Z, entry optional.
            lResult + 1
            ;
         ElseIf *HoldChar\c = 'A'
            ; A - Z or 0 - 9, entry required.
            lResult + 1
            ;
         ElseIf *HoldChar\c = 'a'
            ; A - Z or 0 - 9, entry optional.
            lResult + 1
            ;
         ElseIf *HoldChar\c = '&'
            ; Any character or space, entry required.
            lResult + 1
            ;
         ElseIf *HoldChar\c = 'C'
            ; Any character or space, entry optional.
            lResult + 1
            ;
         ElseIf *HoldChar\c = ''
            ;
            ;
         ElseIf *HoldChar\c = ' '
            ;
            lResult + 1
            ;
         ElseIf *HoldChar\c = '\'
            ; The 'force non-formatting character' character.  Ignore this character and do not increase the maximum length.
            AllowAsNonFormatting = #True
            ; 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.
            lResult + 1
            ; A display character always increases the length.
         EndIf 
         ;
      EndIf
      ;
      CompilerIf #PB_Compiler_Unicode = #True : *HoldChar + 2 : CompilerElse : *HoldChar + 1 : CompilerEndIf
      ;
   Wend
   ;
   ProcedureReturn lResult
   ;
EndProcedure
Procedure.s _xm_StripCharacters(xMask.__s_xMask_Main(), Text.s, IndexBegin.l)
   ; This procedure is used to to strip out unneeded characters in text being inserted into a mask.
   Define.Char *HoldMask
   ;
   Define.Char *HoldChar
   ;
   Define.Char *OutText
   ;
   Define.s HoldString
   ;
   Define.b IsFormatting
   ;
   Define.b FailedMask
   ;
   Define.b ForceAdd
   ;
   *HoldChar = @Text
   ;
   *HoldMask = @xMask()\TextMask + (IndexBegin - 1)
   ;
   HoldString = Space(Len(Text))
   ;
   *OutText = @HoldString
   ;
   While *HoldMask\c And *HoldChar\c
      ;
      FailedMask = #True
      ;
      If *HoldMask\c = '0'
         ; Required numeric field (0-9).
         If *HoldChar\c > 47 And *HoldChar\c < 58 : FailedMask = #False : EndIf
         ;
      ElseIf *HoldMask\c = '9'
         ; Non-required numeric field.  A number (0-9) or a space is allowed.
         If (*HoldChar\c > 47 And *HoldChar\c < 58) Or *HoldChar\c = 32 : FailedMask = #False : EndIf
         ;
      ElseIf *HoldMask\c = 'L'
         ; A - Z, entry required.
         If (*HoldChar\c > 64 And *HoldChar\c < 91) Or (*HoldChar\c > 96 And *HoldChar\c < 123) : FailedMask = #False : EndIf
         ;
      ElseIf *HoldMask\c = '?'
         ; A - Z, entry not required and may include spaces.
         If (*HoldChar\c > 64 And *HoldChar\c < 91) Or (*HoldChar\c > 96 And *HoldChar\c < 123) Or *HoldChar\c = 32 : FailedMask = #False : EndIf
         ;
      ElseIf *HoldMask\c = 'A'
         ; A - Z or 0 - 9, entry required.
         If (*HoldChar\c > 64 And *HoldChar\c < 91) Or (*HoldChar\c > 96 And *HoldChar\c < 123) Or (*HoldChar\c > 47 And *HoldChar\c < 58) : FailedMask = #False : EndIf
         ;
      ElseIf *HoldMask\c = 'a'
         ; A - Z or 0 - 9, entry not required and may include spaces.
         If (*HoldChar\c > 64 And *HoldChar\c < 91) Or (*HoldChar\c > 96 And *HoldChar\c < 123) Or (*HoldChar\c > 47 And *HoldChar\c < 58) Or *HoldChar\c = 32 : FailedMask = #False : EndIf
         ;
      ElseIf *HoldMask\c = '&'
         ; Any character or space, entry required.
         If *HoldChar\c > 32 And *HoldChar\c < 256 : FailedMask = #False : EndIf
         ;
      ElseIf *HoldMask\c = 'C'
         ; Any character or space, entry not required and may include spaces.
         If (*HoldChar\c > 32 And *HoldChar\c < 256) Or *HoldChar\c = 32 : FailedMask = #False : EndIf
         ;
      ElseIf *HoldMask\c = '>'
         ; Capitalize the following character.
         IsFormatting = #True
         ;
      ElseIf *HoldMask\c = '<'
         ; Decapitalize the following character.
         IsFormatting = #True
         ;
      ElseIf *HoldMask\c = '\'
         ; Add the next character as-is.
         IsFormatting = #True
         ;
      Else
         ;
         ForceAdd = #True
         ;
         If *HoldMask\c = *HoldChar\c : FailedMask = #False : EndIf
         ;
      EndIf 
      ;
      If IsFormatting = #False
         ;
         If FailedMask
            ;
            ProcedureReturn Text
            ;
         Else
            ;
            If ForceAdd = #False
               ;
               *OutText\c = *HoldChar\c
               ;
               CompilerIf #PB_Compiler_Unicode = #True : *OutText + 2 : CompilerElse : *OutText + 1 : CompilerEndIf
               ;
            EndIf
            ;
            CompilerIf #PB_Compiler_Unicode = #True : *HoldChar + 2 : CompilerElse : *HoldChar + 1 : CompilerEndIf
            ;
         EndIf
         ;
      EndIf
      ;
      ForceAdd = #False : IsFormatting = #False
      ;
      CompilerIf #PB_Compiler_Unicode = #True : *HoldMask + 2 : CompilerElse : *HoldMask + 1 : CompilerEndIf
      ;
   Wend
   ;
   ProcedureReturn Left(HoldString, *OutText - @HoldString)
   ;
EndProcedure
Procedure.c _xm_ReturnCharacter(xMask.__s_xMask_Main(), Character.c, *Index.l, UsingPlaceholder.b = #False)
   ;
   Define.Char *HoldMask
   ;
   Define.b IsFormatting
   ;
   Define.b FailedMask
   ;
   Define.b ForceAdd
   ;
   Define.l Position
   ;
   Define.b DoUpper, DoLower, IsStatic
   ;
   Define.l HoldIndex
   ; 
   *HoldMask = @xMask()\TextMask
   ;
   While *HoldMask\c
      ;
      FailedMask = #True
      ;
      If ForceAdd
         ;
         IsStatic = #True
         ;
      Else
         ;
         If *HoldMask\c = '0'
            ; Required numeric field (0-9).
            If Character > 47 And Character < 58 : FailedMask = #False : EndIf
            ;
         ElseIf *HoldMask\c = '9'
            ; Non-required numeric field.  A number (0-9) or a space is allowed.
            If (Character > 47 And Character < 58) Or Character = 32 : FailedMask = #False : EndIf
            ;
         ElseIf *HoldMask\c = 'L'
            ; A - Z, entry required.
            If (Character > 64 And Character < 91) Or (Character > 96 And Character < 123) : FailedMask = #False : EndIf
            ;
         ElseIf *HoldMask\c = '?'
            ; A - Z, entry not required and may include spaces.
            If (Character > 64 And Character < 91) Or (Character > 96 And Character < 123) Or Character = 32 : FailedMask = #False : EndIf
            ;
         ElseIf *HoldMask\c = 'A'
            ; A - Z or 0 - 9, entry required.
            If (Character > 64 And Character < 91) Or (Character > 96 And Character < 123) Or (Character > 47 And Character < 58) : FailedMask = #False : EndIf
            ;
         ElseIf *HoldMask\c = 'a'
            ; A - Z or 0 - 9, entry not required and may include spaces.
            If (Character > 64 And Character < 91) Or (Character > 96 And Character < 123) Or (Character > 47 And Character < 58) Or Character = 32 : FailedMask = #False : EndIf
            ;
         ElseIf *HoldMask\c = '&'
            ; Any character or space, entry required.
            If Character > 32 And Character < 256 : FailedMask = #False : EndIf
            ;
         ElseIf *HoldMask\c = 'C'
            ; Any character or space, entry not required and may include spaces.
            If (Character > 32 And Character < 256) Or Character = 32 : FailedMask = #False : EndIf
            ;
         ElseIf *HoldMask\c = '>'
            ; Capitalize the following character.
            IsFormatting = #True
            ;
            DoUpper = #True
            ;
         ElseIf *HoldMask\c = '<'
            ; Decapitalize the following character.
            IsFormatting = #True
            ;
            DoLower = #True
            ;
         ElseIf *HoldMask\c = '\'
            ; Add the next character as-is.
            IsFormatting = #True
            ;
            ForceAdd = #True
            ;
         Else
            ;
            IsStatic = #True
            ;
         EndIf 
         ;
      EndIf
      ;
      ; If IsStatic And UsingPlaceholder : FailedMask = #True : Else : FailedMask = #False : EndIf
      If UsingPlaceholder : FailedMask = IsStatic : EndIf
      ;
      If IsFormatting = #False
         ;
         HoldIndex + 1
         ;
         If IsStatic = #False
            ;
            If Position = PeekL(*Index)
               ;
               PokeL(*Index, HoldIndex - 1)
               ;
               If FailedMask
                  ;
                  ProcedureReturn 0
                  ;
               Else
                  ;
                  If UsingPlaceholder
                     ;
                     ProcedureReturn xMask()\PlaceHolder
                     ;
                  ElseIf DoUpper
                     ;
                     If Character > 96 And Character < 123 : ProcedureReturn Character - 32 : Else : ProcedureReturn Character : EndIf
                     ;
                  ElseIf DoLower
                     ;
                     If Character > 64 And Character < 91 : ProcedureReturn Character + 32 : Else : ProcedureReturn Character : EndIf
                     ;
                  Else
                     ;
                     ProcedureReturn Character
                     ;
                  EndIf
                  ;
               EndIf 
               ;
            EndIf
            ;
            Position + 1
            ;
         Else
            ;
            If UsingPlaceholder
               ;
               If Position = PeekL(*Index)
                  ;
                  PokeL(*Index, HoldIndex - 1)
                  ;
                  ProcedureReturn 0
                  ;
               EndIf
               ;
            EndIf
            ;
            If Position < PeekL(*Index) : Position + 1 : EndIf
            ;
         EndIf
         ;
         DoUpper = #False : DoLower = #False
         ;
      EndIf
      ;
      ForceAdd = #False : IsFormatting = #False : IsStatic = #False
      ;
      CompilerIf #PB_Compiler_Unicode = #True : *HoldMask + 2 : CompilerElse : *HoldMask + 1 : CompilerEndIf 
      ;
   Wend
   ;
   ProcedureReturn 0
   ;
EndProcedure
Procedure.s _xm_MaskToText(xMask.__s_xMask_Main(), Text.s, ForcePlaceHolder.b = #False)
   ; Inserts the mask into a string gadget with the placeholder character - replacing with Text as
   ; possible.  If ForcePlaceHolder is true, the returned string will just be the placeholder.
   Define.Char *HoldChar
   ;
   Define.Char *HoldMask
   ;
   Define.Char *OutText
   ;
   Define.s HoldString
   ;
   Define.b IgnoreMask
   ; True if the last character was '\' - the next character is treated as an output character.
   Define.b FailedMask
   ; True if the mask conditions are failed when inserting text.
   Define.b IsFormatting
   ;
   Define.b DoUpper, DoLower, ForceAdd
   ;
   *HoldMask = @xMask()\TextMask
   ;
   *HoldChar = @Text
   ;
   HoldString = Space(xMask()\MaxLength)
   ;
   *OutText = @HoldString
   ;
   While *HoldMask\c
      ; Loop until the end-of-line character is located.
      FailedMask = #True
      ; The mask fails by default.
      IsFormatting = #False
      ; A formatting character is the '>' or '<' or '\' character.
      If *HoldMask\c = '0'
         ; Required numeric field (0-9).
         If *HoldChar\c > 47 And *HoldChar\c < 58 : FailedMask = #False : EndIf
         ;
      ElseIf *HoldMask\c = '9'
         ; Non-required numeric field.  A number (0-9) or a space is allowed.
         If (*HoldChar\c > 47 And *HoldChar\c < 58) Or *HoldChar\c = 32 : FailedMask = #False : EndIf
         ;
      ElseIf *HoldMask\c = 'L'
         ; A - Z, entry required.
         If (*HoldChar\c > 64 And *HoldChar\c < 91) Or (*HoldChar\c > 96 And *HoldChar\c < 123) : FailedMask = #False : EndIf
         ;
      ElseIf *HoldMask\c = '?'
         ; A - Z, entry not required and may include spaces.
         If (*HoldChar\c > 64 And *HoldChar\c < 91) Or (*HoldChar\c > 96 And *HoldChar\c < 123) Or *HoldChar\c = 32 : FailedMask = #False : EndIf
         ;
      ElseIf *HoldMask\c = 'A'
         ; A - Z or 0 - 9, entry required.
         If (*HoldChar\c > 64 And *HoldChar\c < 91) Or (*HoldChar\c > 96 And *HoldChar\c < 123) Or (*HoldChar\c > 47 And *HoldChar\c < 58) : FailedMask = #False : EndIf
         ;
      ElseIf *HoldMask\c = 'a'
         ; A - Z or 0 - 9, entry not required and may include spaces.
         If (*HoldChar\c > 64 And *HoldChar\c < 91) Or (*HoldChar\c > 96 And *HoldChar\c < 123) Or (*HoldChar\c > 47 And *HoldChar\c < 58) Or *HoldChar\c = 32 : FailedMask = #False : EndIf
         ;
      ElseIf *HoldMask\c = '&'
         ; Any character or space, entry required.
         If *HoldChar\c > 32 And *HoldChar\c < 256 : FailedMask = #False : EndIf
         ;
      ElseIf *HoldMask\c = 'C'
         ; Any character or space, entry not required and may include spaces.
         If (*HoldChar\c > 32 And *HoldChar\c < 256) Or *HoldChar\c = 32 : FailedMask = #False : EndIf
         ;
      ElseIf *HoldMask\c = '>'
         ; Capitalize the following character.
         IsFormatting = #True
         ;
         DoUpper = #True
         ; 
      ElseIf *HoldMask\c = '<'
         ; Decapitalize the following character.
         IsFormatting = #True
         ;
         DoLower = #True
         ;
      ElseIf *HoldMask\c = '\'
         ; Add the next character as-is.
         IsFormatting = #True
         ;
         ForceAdd = #True
         ;
      Else
         ;
         ForceAdd = #True
         ;
         FailedMask = #False
         ;
      EndIf
      ;
      If IsFormatting = #False Or ForcePlaceHolder
         ;
         If ForcePlaceHolder Or *HoldChar\c = 0 : FailedMask = #False : EndIf
         ;
         If FailedMask
            ; The text does not match the mask.
            ProcedureReturn ""
            ; Return an empty string.
         Else 
            ;
            If ForceAdd
               ;
               *OutText\c = *HoldMask\c
               ;
            ElseIf ForcePlaceHolder Or *HoldChar\c = 0
               ;
               *OutText\c = xMask()\PlaceHolder
               ;
            Else
               ;
               If DoUpper
                  ;
                  *OutText\c = Asc(UCase(Mid(Text, *HoldChar - @Text + 1, 1)))
                  ;
               ElseIf DoLower
                  ;
                  *OutText\c = Asc(LCase(Mid(Text, *HoldChar - @Text + 1, 1)))
                  ;
               Else
                  ;
                  *OutText\c = *HoldChar\c
                  ; Store the current character.
               EndIf
               ;
               CompilerIf #PB_Compiler_Unicode = #True : *HoldChar + 2 : CompilerElse : *HoldChar + 1 : CompilerEndIf
               ;
            EndIf
            ;
            CompilerIf #PB_Compiler_Unicode = #True : *OutText + 2 : CompilerElse : *OutText + 1 : CompilerEndIf
            ; Point to the next available space.
         EndIf
         ;
         DoUpper = #False : DoLower = #False : ForceAdd = #False
         ;
      EndIf
      ;
      CompilerIf #PB_Compiler_Unicode = #True : *HoldMask + 2 : CompilerElse : *HoldMask + 1 : CompilerEndIf
      ;
   Wend
   ;
   ProcedureReturn Left(HoldString, *OutText - @HoldString)
   ;
EndProcedure
Procedure _xm_ClearText(xMask.__s_xMask_Main(), OnlyHighlighted = #False)
   ;
   Define.c Character
   ;
   Define.s HoldString
   ;
   Define.l iLoop, HoldIndex
   ;
   Define.POINT HoldSelect
   ;
   SendMessage_(xMask()\Handle, #EM_GETSEL, @HoldSelect\X, @HoldSelect\Y)
   ; Retrieve the start and end selection values.  We'll use this when replacing characters.
   If OnlyHighlighted And HoldSelect\X = HoldSelect\Y : ProcedureReturn : EndIf
   ;
   If HoldSelect\X < xMask()\MaxLength
      ;
      If HoldSelect\X < HoldSelect\Y : HoldSelect\Y - 1 : EndIf
      ;
      HoldString = GetGadgetText(xMask()\Gadget)
      ;
      For iLoop = HoldSelect\X To HoldSelect\Y
         ;
         HoldIndex = iLoop
         ;
         Character = _xm_ReturnCharacter(xMask(), 0, @HoldIndex, #True)
         ;
         If Character : InsertCharacter(@HoldString, HoldIndex, Character) : EndIf
         ;
      Next iLoop
      ;
      xMask()\AdjustingText = #True
      ;
      SetGadgetText(xMask()\Gadget, HoldString)
      ;
      xMask()\AdjustingText = #False
      ;
      SendMessage_(xMask()\Handle, #EM_SETSEL, HoldSelect\X, HoldSelect\X)
      ;
   EndIf
   ;
EndProcedure
;-
Procedure _xm_EditCallback(HandleWindow.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.
   Define.b CaughtMatch
   ;
   Define.l iLoop
   ;
   Define.l lResult
   ;
   Define.l HoldStyle
   ;
   Define.s HoldString, HoldText
   ;
   Define.POINT HoldSelect
   ;
   Define.c Character
   ;
   Define.l HoldIndex
   ;
   ResetList(__xm_Main())
   While NextElement(__xm_Main())
      ;
      If __xm_Main()\Handle = HandleWindow : CaughtMatch = #True : Break : EndIf
      ;
   Wend
   ;
   If CaughtMatch = #False : ProcedureReturn : EndIf
   ; This should never happen but exit if the string gadget is not on the list.
   HoldStyle = GetWindowLong_(__xm_Main()\Handle, #GWL_STYLE)
   ; Get the style of the string gadget.
   If HoldStyle & #ES_READONLY = 0
      ; Ensure the string gadget is not read only.
      If message = #WM_SETTEXT
         ;{ Text is changing via a 'SetGadgetText()' call.
         If __xm_Main()\AdjustingText = #False
            ;
            HoldString = _xm_MaskToText(__xm_Main(), _xm_StripCharacters(__xm_Main(), PeekS(lParam), 1), #False)
            ; Check the text against the mask and store the result.  lParam is the pointer to the updating text.
            If HoldString <> ""
               ;
               __xm_Main()\AdjustingText = #True
               ;
               SetGadgetText(__xm_Main()\Gadget, HoldString)
               ; Update the text in the control.
               __xm_Main()\AdjustingText = #False
               ;
            EndIf
            ;
            message = 0
            ; Clear the message - the text is handled internally.
         EndIf
         ;}
      ElseIf message = #WM_SETFOCUS
         ; The edit control received focus.
         If __xm_Main()\SelectAllOnFocus : SendMessage_(__xm_Main()\Handle, #EM_SETSEL, 0, -1) : EndIf
         ; Select all text in the edit control.
      ElseIf message = #WM_CHAR
         ;{
         If wParam = #VK_BACK
            ;
            SendMessage_(__xm_Main()\Handle, #EM_GETSEL, @HoldSelect\X, @HoldSelect\Y)
            ; Retrieve the start and end selection values.  We'll use this when replacing characters.
            If HoldSelect\X
               ;
               If HoldSelect\X = HoldSelect\Y : HoldSelect\X - 1 : HoldSelect\Y - 1 : EndIf
               ;
               HoldString = GetGadgetText(__xm_Main()\Gadget)
               ;
               For iLoop = HoldSelect\X To HoldSelect\Y
                  ;
                  HoldIndex = iLoop
                  ;
                  Character = _xm_ReturnCharacter(__xm_Main(), wParam, @HoldIndex, #True)
                  ;
                  If Character : InsertCharacter(@HoldString, HoldIndex, Character) : EndIf
                  ;
               Next iLoop
               ;
               __xm_Main()\AdjustingText = #True
               ;
               SetGadgetText(__xm_Main()\Gadget, HoldString)
               ;
               __xm_Main()\AdjustingText = #False
               ;
               SendMessage_(__xm_Main()\Handle, #EM_SETSEL, HoldSelect\X, HoldSelect\X)
               ;
            EndIf
            ;
         Else
            ;
            SendMessage_(__xm_Main()\Handle, #EM_GETSEL, @HoldSelect\X, @HoldSelect\Y)
            ; Retrieve the start and end selection values.  We'll use this when replacing characters.
            If HoldSelect\X <= __xm_Main()\MaxLength
               ; Do not allow any text past the maximum length.
               HoldIndex = HoldSelect\X
               ;
               Character = _xm_ReturnCharacter(__xm_Main(), wParam, @HoldSelect\X)
               ;
               If Character
                  ;
                  HoldString = GetGadgetText(__xm_Main()\Gadget)
                  ;
                  InsertCharacter(@HoldString, HoldSelect\X, Character)
                  ;
                  __xm_Main()\AdjustingText = #True
                  ;
                  SetGadgetText(__xm_Main()\Gadget, HoldString)
                  ;
                  __xm_Main()\AdjustingText = #False
                  ;
                  SendMessage_(__xm_Main()\Handle, #EM_SETSEL, HoldSelect\X + 1, HoldSelect\X + 1) ; HoldSelect\y + (HoldSelect\x - HoldIndex) + 1)
                  ;
               EndIf
               ;
            EndIf
            ;
         EndIf
         ;
         wParam = 0
         ; Flush the charcter.
         ;}
      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
            ;
            _xm_ClearText(__xm_Main()) : wParam = 0 
            ;
         EndIf
         ;}
      ElseIf message = #WM_CLEAR
         ;{ The delete submenu from the right-click menu is called.
         _xm_ClearText(__xm_Main(), #True) : message = 0
         ;
         ;}
      ElseIf message = #WM_CUT
         ;{ The cut submenu from the right-click menu is called.
         SendMessage_(__xm_Main()\Handle, #EM_GETSEL, @HoldSelect\X, @HoldSelect\Y)
         ; Retrieve the start and end selection values.  We'll use this when replacing characters.
         If HoldSelect\X = HoldSelect\Y : ProcedureReturn : EndIf
         ;
         HoldString = Mid(GetGadgetText(__xm_Main()\Gadget), HoldSelect\X + 1, HoldSelect\Y - HoldSelect\X)
         ;
         SetClipboardText(HoldString)
         ;
         _xm_ClearText(__xm_Main(), #True) : message = 0
         ;
         ;}
      ElseIf message = #WM_PASTE
         ;{ The paste submenu from the right-click menu is called.
         SendMessage_(__xm_Main()\Handle, #EM_GETSEL, @HoldSelect\X, @HoldSelect\Y)
         ; Retrieve the start and end selection values.  We'll use this when replacing characters.
         If HoldSelect\X = HoldSelect\Y : ProcedureReturn : EndIf
         ;
         HoldString = GetGadgetText(__xm_Main()\Gadget)
         ;
         HoldText = _xm_StripCharacters(__xm_Main(), GetClipboardText(), HoldSelect\X + 1)
         ;
         HoldIndex = 1
         ;
         For iLoop = HoldSelect\X To HoldSelect\Y
            ;
            Character = _xm_ReturnCharacter(__xm_Main(), Asc(Mid(HoldText, HoldIndex, 1)), @iLoop, #False)
            ;
            If Character : InsertCharacter(@HoldString, iLoop, Character) : EndIf
            ;
            HoldIndex + 1
            ;
         Next iLoop
         ;
         __xm_Main()\AdjustingText = #True
         ;
         SetGadgetText(__xm_Main()\Gadget, HoldString)
         ; Update the text in the control.
         __xm_Main()\AdjustingText = #False
         ;
         SendMessage_(__xm_Main()\Handle, #EM_SETSEL, HoldSelect\Y, HoldSelect\Y)
         ;
         message = 0
         ;
         ;}
      EndIf
      ;
   EndIf
   ;
   lResult = CallWindowProc_(__xm_Main()\OldCallback, HandleWindow, message, wParam, lParam)
   ; Call the original edit control's callback function for any messages not handled.
   ProcedureReturn lResult
   ;
EndProcedure
;-
Procedure.s xm_GetRawData(Gadget.l)
   ;
   ResetList(__xm_Main())
   While NextElement(__xm_Main())
      ;
      If __xm_Main()\Gadget = Gadget
         ;
         ProcedureReturn _xm_StripCharacters(__xm_Main(), GetGadgetText(__xm_Main()\Gadget), 1)
         ;
      EndIf
      ;
   Wend
   ;
   ProcedureReturn ""
   ;
EndProcedure
Procedure RemoveGadgetMasks(Gadget.l) ; Use 0 to remove all masks.
   ;
   ResetList(__xm_Main())
   While NextElement(__xm_Main())
      ;
      If Gadget = 0 Or Gadget = __xm_Main()\Gadget
         ;
         SetWindowLong_(__xm_Main()\Handle, #GWL_WNDPROC, __xm_Main()\OldCallback)
         ; Now set the custom callback to use for the new xMask gadget.
         DeleteElement(__xm_Main())
         ;
      EndIf
      ;
   Wend
   ;
EndProcedure
Procedure SetGadgetMask(Gadget.l, TextMask.s, Text.s, PlaceHolder.b, RightToLeft.b = #False, SelectAllOnFocus.b = #False)
   ; Modifies a string gadget to only accept certain characters based on a mask.
   Define.s HoldString
   ; This will store the length of the wide character string, in characters. 
   Define.l lCount
   ; Used to store the number of character copied. 
   Define.l Handle
   ; The handle of the gadget.
   Handle = GadgetID(Gadget)
   ; Get the handle of the gadget.
   HoldString = Space(255) 
   ; Allocate size for our string. 
   lCount = GetClassName_(Handle, @HoldString, 255) 
   ; Call our function to retrieve the classname. 
   HoldString = LCase(Left(HoldString, lCount))
   ; Truncate the string based on the number of characters copied.
   If HoldString = "edit"
      ; Only edit controls may be masked.
      ResetList(__xm_Main())
      While NextElement(__xm_Main())
         ;
         If __xm_Main()\Gadget = Gadget : ProcedureReturn : EndIf
         ; Do not allow duplicates.
      Wend
      ;
      AddElement(__xm_Main())
      ; Add a new xMask control.
      With __xm_Main()
         ;
         \Gadget = Gadget
         \Handle = Handle
         ;
         \TextMask = TextMask
         \Text = Text
         ;
         \PlaceHolder = PlaceHolder
         \RightToLeft = RightToLeft
         ;
         \MaxLength = _xm_GetMaxLength(TextMask)
         ; Get the maximum allowed length of the edit control.
         \SelectAllOnFocus = SelectAllOnFocus
         ;
      EndWith
      ;
      HoldString = _xm_MaskToText(__xm_Main(), Text, #False)
      ;
      If HoldString = "" : HoldString = _xm_MaskToText(__xm_Main(), HoldString, #True) : EndIf
      ;
      SetGadgetText(Gadget, HoldString)
      ;
      SendMessage_(Handle, #EM_LIMITTEXT, __xm_Main()\MaxLength, 0)
      ; Set the maximum number of allowed characters.
      __xm_Main()\OldCallback = GetWindowLong_(__xm_Main()\Handle, #GWL_WNDPROC)
      ; Store the original callback procedure address for the Edit control.
      SetWindowLong_(__xm_Main()\Handle, #GWL_WNDPROC, @_xm_EditCallback())
      ; Now set the custom callback to use for the new xMask gadget.
   EndIf
   ;
EndProcedure
;-
;{ Create Test Window
If OpenWindow(0, 0, 0, 400, 300, "Test", #PB_Window_TitleBar | #PB_Window_SystemMenu)
   ;
   If CreateGadgetList(WindowID(0))
      ;
      StringGadget(0, 10, 10, 120, 20, "")
      ;
      SetGadgetMask(0, "999-99-9999", "123456789", '*', #False)
      ;
   EndIf
   ;
EndIf
;
Define.b DoQuit
;
Define.l HoldID
;
While DoQuit = #False
   ;
   HoldID = WaitWindowEvent()
   ;
   If HoldID = #PB_Event_CloseWindow
      ; Close the program.
      DoQuit = #True
      ;
   EndIf
   ;
Wend
;}
;-
RemoveGadgetMasks(0)
;-
End
;-
It's just one file. Good luck and thanks to ebs for noticing the error. This post fixes it.
STU-ASI
User
User
Posts: 24
Joined: Thu Dec 08, 2005 10:56 am

Post by STU-ASI »

Great code Xombie..

Did the problem of the beep or pc speaker ever get sorted??

It appears as though the code causes a windows sound to play, its actually the default beep sound when a key is pressed.

Verified this on 3 different pc all using xp.
ebs
Enthusiast
Enthusiast
Posts: 557
Joined: Fri Apr 25, 2003 11:08 pm

Post by ebs »

Did the problem of the beep or pc speaker ever get sorted??
If you add the statement "message = 0" as shown, it eliminates the beep:

Code: Select all

      wParam = 0
      message = 0
      ; Flush the character.
I don't believe it affects the operation of the code.

Regards,
Eric
STU-ASI
User
User
Posts: 24
Joined: Thu Dec 08, 2005 10:56 am

Post by STU-ASI »

Thanks.

All fixed now and working great.
Post Reply