1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2007 Novell, Inc. (http://www.novell.com)
23 // Rolf Bjarne Kvinge <RKvinge@novell.com>
31 0 Digit, required. This element will accept any single digit between 0 and 9.
32 9 Digit or space, optional.
33 # Digit or space, optional. If this position is blank in the mask, it will be rendered as a space in the Text property. Plus (+) and minus (-) signs are allowed.
34 L Letter, required. Restricts input to the ASCII letters a-z and A-Z. This mask element is equivalent to [a-zA-Z] in regular expressions.
35 ? Letter, optional. Restricts input to the ASCII letters a-z and A-Z. This mask element is equivalent to [a-zA-Z]? in regular expressions.
36 & Character, required. If the AsciiOnly property is set to true, this element behaves like the "L" element.
37 C Character, optional. Any non-control character. If the AsciiOnly property is set to true, this element behaves like the "?" element.
38 * LAMESPEC: A is REQUIRED, not optional.
39 A Alphanumeric, optional. If the AsciiOnly property is set to true, the only characters it will accept are the ASCII letters a-z and A-Z.
40 a Alphanumeric, optional. If the AsciiOnly property is set to true, the only characters it will accept are the ASCII letters a-z and A-Z.
41 . Decimal placeholder. The actual display character used will be the decimal symbol appropriate to the format provider, as determined by the control's FormatProvider property.
42 , Thousands placeholder. The actual display character used will be the thousands placeholder appropriate to the format provider, as determined by the control's FormatProvider property.
43 : Time separator. The actual display character used will be the time symbol appropriate to the format provider, as determined by the control's FormatProvider property.
44 / Date separator. The actual display character used will be the date symbol appropriate to the format provider, as determined by the control's FormatProvider property.
45 $ Currency symbol. The actual character displayed will be the currency symbol appropriate to the format provider, as determined by the control's FormatProvider property.
46 < Shift down. Converts all characters that follow to lowercase.
47 > Shift up. Converts all characters that follow to uppercase.
48 | Disable a previous shift up or shift down.
49 \ Escape. Escapes a mask character, turning it into a literal. "\\" is the escape sequence for a backslash.
54 using System
.Globalization
;
55 using System
.Collections
;
56 using System
.Diagnostics
;
59 namespace System
.ComponentModel
{
60 public class MaskedTextProvider
: ICloneable
62 #region Private fields
63 private bool allow_prompt_as_input
;
64 private bool ascii_only
;
65 private CultureInfo culture
;
66 private bool include_literals
;
67 private bool include_prompt
;
68 private bool is_password
;
70 private char password_char
;
71 private char prompt_char
;
72 private bool reset_on_prompt
;
73 private bool reset_on_space
;
74 private bool skip_literals
;
76 private EditPosition
[] edit_positions
;
78 private static char default_prompt_char
= '_';
79 private static char default_password_char
= char.MinValue
;
83 #region Private classes
84 private enum EditState
{
89 private enum EditType
{
91 DigitOrSpaceOptional
, // 9
92 DigitOrSpaceOptional_Blank
, // #
95 CharacterRequired
, // &
96 CharacterOptional
, // C
97 AlphanumericRequired
, // A
98 AlphanumericOptional
, // a
99 DecimalPlaceholder
, // .
100 ThousandsPlaceholder
, // ,
107 private class EditPosition
{
108 public MaskedTextProvider Parent
;
109 public EditType Type
;
110 public EditState State
;
111 public char MaskCharacter
;
116 input
= char.MinValue
;
119 internal EditPosition
Clone () {
120 EditPosition result
= new EditPosition ();
121 result
.Parent
= Parent
;
123 result
.State
= State
;
124 result
.MaskCharacter
= MaskCharacter
;
125 result
.input
= input
;
135 case EditState
.LowerCase
:
136 input
= char.ToLower (value, Parent
.Culture
);
138 case EditState
.UpperCase
:
139 input
= char.ToUpper (value, Parent
.Culture
);
141 default:// case EditState.None:
148 public bool IsAscii (char c
)
150 return ((c
>= 'a' && c
<= 'z') || (c
>= 'A' && c
<= 'Z'));
153 public bool Match (char c
, out MaskedTextResultHint resultHint
, bool only_test
)
155 if (!MaskedTextProvider
.IsValidInputChar (c
)) {
156 resultHint
= MaskedTextResultHint
.InvalidInput
;
160 if (Parent
.ResetOnSpace
&& c
== ' ' && Editable
) {
161 resultHint
= MaskedTextResultHint
.CharacterEscaped
;
163 resultHint
= MaskedTextResultHint
.Success
;
164 if (!only_test
&& input
!= ' ') {
166 case EditType
.AlphanumericOptional
:
167 case EditType
.AlphanumericRequired
:
168 case EditType
.CharacterOptional
:
169 case EditType
.CharacterRequired
:
173 Input
= char.MinValue
;
181 if (Type
== EditType
.Literal
&& MaskCharacter
== c
&& Parent
.SkipLiterals
) {
182 resultHint
= MaskedTextResultHint
.Success
;
187 resultHint
= MaskedTextResultHint
.NonEditPosition
;
192 case EditType
.AlphanumericOptional
:
193 case EditType
.AlphanumericRequired
:
194 if (char.IsLetterOrDigit (c
)) {
195 if (Parent
.AsciiOnly
&& !IsAscii (c
)) {
196 resultHint
= MaskedTextResultHint
.AsciiCharacterExpected
;
202 resultHint
= MaskedTextResultHint
.Success
;
206 resultHint
= MaskedTextResultHint
.AlphanumericCharacterExpected
;
209 case EditType
.CharacterOptional
:
210 case EditType
.CharacterRequired
:
211 if (Parent
.AsciiOnly
&& !IsAscii (c
)) {
212 resultHint
= MaskedTextResultHint
.LetterExpected
;
214 } else if (!char.IsControl (c
)) {
218 resultHint
= MaskedTextResultHint
.Success
;
221 resultHint
= MaskedTextResultHint
.LetterExpected
;
224 case EditType
.DigitOrSpaceOptional
:
225 case EditType
.DigitOrSpaceOptional_Blank
:
226 if (char.IsDigit (c
) || c
== ' ') {
230 resultHint
= MaskedTextResultHint
.Success
;
233 resultHint
= MaskedTextResultHint
.DigitExpected
;
236 case EditType
.DigitRequired
:
237 if (char.IsDigit (c
)) {
241 resultHint
= MaskedTextResultHint
.Success
;
244 resultHint
= MaskedTextResultHint
.DigitExpected
;
247 case EditType
.LetterOptional
:
248 case EditType
.LetterRequired
:
249 if (!char.IsLetter (c
)) {
250 resultHint
= MaskedTextResultHint
.LetterExpected
;
252 } else if (Parent
.AsciiOnly
&& !IsAscii (c
)) {
253 resultHint
= MaskedTextResultHint
.LetterExpected
;
259 resultHint
= MaskedTextResultHint
.Success
;
263 resultHint
= MaskedTextResultHint
.Unknown
;
268 public bool FilledIn
{
270 return Input
!= char.MinValue
;
274 public bool Required
{
276 switch (MaskCharacter
) {
288 public bool Editable
{
290 switch (MaskCharacter
) {
307 public bool Visible
{
309 switch (MaskCharacter
) {
321 if (Type
== EditType
.Literal
) {
322 return MaskCharacter
.ToString ();
324 switch (MaskCharacter
) {
325 case '.': return Parent
.Culture
.NumberFormat
.NumberDecimalSeparator
;
326 case ',': return Parent
.Culture
.NumberFormat
.NumberGroupSeparator
;
327 case ':': return Parent
.Culture
.DateTimeFormat
.TimeSeparator
;
328 case '/': return Parent
.Culture
.DateTimeFormat
.DateSeparator
;
329 case '$': return Parent
.Culture
.NumberFormat
.CurrencySymbol
;
331 return FilledIn
? Input
.ToString () : Parent
.PromptChar
.ToString ();
336 private EditPosition ()
340 public EditPosition (MaskedTextProvider Parent
, EditType Type
, EditState State
, char MaskCharacter
)
343 this.Parent
= Parent
;
345 this.MaskCharacter
= MaskCharacter
;
351 #region Private methods & properties
352 private void SetMask (string mask
)
354 if (mask
== null || mask
== string.Empty
)
355 throw new ArgumentException ("The Mask value cannot be null or empty.\r\nParameter name: mask");
359 System
.Collections
.Generic
.List
<EditPosition
> list
= new System
.Collections
.Generic
.List
<EditPosition
> (mask
.Length
);
361 EditState State
= EditState
.None
;
362 bool escaped
= false;
363 for (int i
= 0; i
< mask
.Length
; i
++) {
365 list
.Add (new EditPosition (this, EditType
.Literal
, State
, mask
[i
]));
375 list
.Add (new EditPosition (this, EditType
.DigitRequired
, State
, mask
[i
]));
378 list
.Add (new EditPosition (this, EditType
.DigitOrSpaceOptional
, State
, mask
[i
]));
381 list
.Add (new EditPosition (this, EditType
.DigitOrSpaceOptional_Blank
, State
, mask
[i
]));
384 list
.Add (new EditPosition (this, EditType
.LetterRequired
, State
, mask
[i
]));
387 list
.Add (new EditPosition (this, EditType
.LetterOptional
, State
, mask
[i
]));
390 list
.Add (new EditPosition (this, EditType
.CharacterRequired
, State
, mask
[i
]));
393 list
.Add (new EditPosition (this, EditType
.CharacterOptional
, State
, mask
[i
]));
396 list
.Add (new EditPosition (this, EditType
.AlphanumericRequired
, State
, mask
[i
]));
399 list
.Add (new EditPosition (this, EditType
.AlphanumericOptional
, State
, mask
[i
]));
402 list
.Add (new EditPosition (this, EditType
.DecimalPlaceholder
, State
, mask
[i
]));
405 list
.Add (new EditPosition (this, EditType
.ThousandsPlaceholder
, State
, mask
[i
]));
408 list
.Add (new EditPosition (this, EditType
.TimeSeparator
, State
, mask
[i
]));
411 list
.Add (new EditPosition (this, EditType
.DateSeparator
, State
, mask
[i
]));
414 list
.Add (new EditPosition (this, EditType
.CurrencySymbol
, State
, mask
[i
]));
417 State
= EditState
.LowerCase
;
420 State
= EditState
.UpperCase
;
423 State
= EditState
.None
;
426 list
.Add (new EditPosition (this, EditType
.Literal
, State
, mask
[i
]));
430 edit_positions
= list
.ToArray ();
433 private EditPosition
[] ClonePositions ()
435 EditPosition
[] result
= new EditPosition
[edit_positions
.Length
];
436 for (int i
= 0; i
< result
.Length
; i
++) {
437 result
[i
] = edit_positions
[i
].Clone ();
442 private bool AddInternal (string str_input
, out int testPosition
, out MaskedTextResultHint resultHint
, bool only_test
)
444 EditPosition
[] edit_positions
;
447 edit_positions
= ClonePositions ();
449 edit_positions
= this.edit_positions
;
452 if (str_input
== null)
453 throw new ArgumentNullException ("input");
455 if (str_input
.Length
== 0) {
456 resultHint
= MaskedTextResultHint
.NoEffect
;
457 testPosition
= LastAssignedPosition
+ 1;
461 resultHint
= MaskedTextResultHint
.Unknown
;
464 int next_position
= LastAssignedPosition
;
465 MaskedTextResultHint tmpResult
= MaskedTextResultHint
.Unknown
;
467 if (next_position
>= edit_positions
.Length
) {
468 testPosition
= next_position
;
469 resultHint
= MaskedTextResultHint
.UnavailableEditPosition
;
473 for (int i
= 0; i
< str_input
.Length
; i
++) {
474 char input
= str_input
[i
];
476 testPosition
= next_position
;
478 if (tmpResult
> resultHint
) {
479 resultHint
= tmpResult
;
482 if (VerifyEscapeChar (input
, next_position
)) {
483 tmpResult
= MaskedTextResultHint
.CharacterEscaped
;
487 next_position
= FindEditPositionFrom (next_position
, true);
488 testPosition
= next_position
;
490 if (next_position
== InvalidIndex
) {
491 testPosition
= edit_positions
.Length
;
492 resultHint
= MaskedTextResultHint
.UnavailableEditPosition
;
496 if (!IsValidInputChar (input
)) {
497 testPosition
= next_position
;
498 resultHint
= MaskedTextResultHint
.InvalidInput
;
502 if (!edit_positions
[next_position
].Match (input
, out tmpResult
, false)) {
503 testPosition
= next_position
;
504 resultHint
= tmpResult
;
509 if (tmpResult
> resultHint
) {
510 resultHint
= tmpResult
;
516 private bool AddInternal (char input
, out int testPosition
, out MaskedTextResultHint resultHint
, bool check_available_positions_first
, bool check_escape_char_first
)
519 * check_available_positions_first: when adding a char, MS first seems to check if there are any available edit positions, then if there are any
520 * they will try to add the char (meaning that if you add a char matching a literal char in the mask with SkipLiterals and there are no
521 * more available edit positions, it will fail).
523 * check_escape_char_first: When adding a char, MS doesn't check for escape char, they directly search for the first edit position.
524 * When adding a string, they check first for escape char, then if not successful they find the first edit position.
530 new_position
= LastAssignedPosition
+ 1;
532 if (check_available_positions_first
) {
533 int tmp
= new_position
;
534 bool any_available
= false;
535 while (tmp
< edit_positions
.Length
) {
536 if (edit_positions
[tmp
].Editable
) {
537 any_available
= true;
542 if (!any_available
) {
544 resultHint
= MaskedTextResultHint
.UnavailableEditPosition
;
545 return GetOperationResultFromHint (resultHint
);
549 if (check_escape_char_first
) {
550 if (VerifyEscapeChar (input
, new_position
)) {
551 testPosition
= new_position
;
552 resultHint
= MaskedTextResultHint
.CharacterEscaped
;
557 new_position
= FindEditPositionFrom (new_position
, true);
559 if (new_position
> edit_positions
.Length
- 1 ||new_position
== InvalidIndex
) {
560 testPosition
= new_position
;
561 resultHint
= MaskedTextResultHint
.UnavailableEditPosition
;
562 return GetOperationResultFromHint (resultHint
);
565 if (!IsValidInputChar (input
)) {
566 testPosition
= new_position
;
567 resultHint
= MaskedTextResultHint
.InvalidInput
;
568 return GetOperationResultFromHint (resultHint
);
571 if (!edit_positions
[new_position
].Match (input
, out resultHint
, false)) {
572 testPosition
= new_position
;
573 return GetOperationResultFromHint (resultHint
);
576 testPosition
= new_position
;
578 return GetOperationResultFromHint (resultHint
);
581 private bool VerifyStringInternal (string input
, out int testPosition
, out MaskedTextResultHint resultHint
, int startIndex
, bool only_test
)
583 int previous_position
= startIndex
;
584 int current_position
;
585 resultHint
= MaskedTextResultHint
.Unknown
;
587 // Replace existing characters
588 for (int i
= 0; i
< input
.Length
; i
++) {
589 MaskedTextResultHint tmpResult
;
590 current_position
= FindEditPositionFrom (previous_position
, true);
591 if (current_position
== InvalidIndex
) {
592 testPosition
= edit_positions
.Length
;
593 resultHint
= MaskedTextResultHint
.UnavailableEditPosition
;
597 if (!VerifyCharInternal (input
[i
], current_position
, out tmpResult
, only_test
)) {
598 testPosition
= current_position
;
599 resultHint
= tmpResult
;
602 if (tmpResult
> resultHint
) {
603 resultHint
= tmpResult
;
605 previous_position
= current_position
+ 1;
608 // Remove characters not in the input.
610 previous_position
= FindEditPositionFrom (previous_position
, true);
611 while (previous_position
!= InvalidIndex
) {
612 if (edit_positions
[previous_position
].FilledIn
) {
613 edit_positions
[previous_position
].Reset ();
614 if (resultHint
!= MaskedTextResultHint
.NoEffect
) {
615 resultHint
= MaskedTextResultHint
.Success
;
618 previous_position
= FindEditPositionFrom (previous_position
+ 1, true);
621 if (input
.Length
> 0) {
622 testPosition
= startIndex
+ input
.Length
- 1;
624 testPosition
= startIndex
;
625 if (resultHint
< MaskedTextResultHint
.NoEffect
) {
626 resultHint
= MaskedTextResultHint
.NoEffect
;
633 private bool VerifyCharInternal (char input
, int position
, out MaskedTextResultHint hint
, bool only_test
)
635 hint
= MaskedTextResultHint
.Unknown
;
637 if (position
< 0 || position
>= edit_positions
.Length
) {
638 hint
= MaskedTextResultHint
.PositionOutOfRange
;
642 if (!IsValidInputChar (input
)) {
643 hint
= MaskedTextResultHint
.InvalidInput
;
647 if (input
== ' ' && ResetOnSpace
&& edit_positions
[position
].Editable
&& edit_positions
[position
].FilledIn
) {
649 edit_positions
[position
].Reset ();
651 hint
= MaskedTextResultHint
.SideEffect
;
655 if (edit_positions
[position
].Editable
&& edit_positions
[position
].FilledIn
&& edit_positions
[position
].input
== input
) {
656 hint
= MaskedTextResultHint
.NoEffect
;
660 if (SkipLiterals
&& !edit_positions
[position
].Editable
&& edit_positions
[position
].Text
== input
.ToString ()) {
661 hint
= MaskedTextResultHint
.CharacterEscaped
;
665 return edit_positions
[position
].Match (input
, out hint
, only_test
);
668 // Test to see if the input string can be inserted at the specified position.
669 // Does not try to move characters.
670 private bool IsInsertableString (string str_input
, int position
, out int testPosition
, out MaskedTextResultHint resultHint
)
672 int current_position
= position
;
675 resultHint
= MaskedTextResultHint
.UnavailableEditPosition
;
676 testPosition
= InvalidIndex
;
678 for (int i
= 0; i
< str_input
.Length
; i
++) {
679 char ch
= str_input
[i
];
681 test_position
= FindEditPositionFrom (current_position
, true);
683 if (test_position
!= InvalidIndex
&& VerifyEscapeChar (ch
, test_position
)) {
684 current_position
= test_position
+ 1;
688 if (VerifyEscapeChar (ch
, current_position
)) {
689 current_position
= current_position
+ 1;
694 if (test_position
== InvalidIndex
) {
695 resultHint
= MaskedTextResultHint
.UnavailableEditPosition
;
696 testPosition
= edit_positions
.Length
;
700 testPosition
= test_position
;
702 if (!edit_positions
[test_position
].Match (ch
, out resultHint
, true)) {
706 current_position
= test_position
+ 1;
708 resultHint
= MaskedTextResultHint
.Success
;
713 private bool ShiftPositionsRight (EditPosition
[] edit_positions
, int start
, out int testPosition
, out MaskedTextResultHint resultHint
)
716 int last_assigned_index
= FindAssignedEditPositionFrom (edit_positions
.Length
, false);
717 int endindex
= FindUnassignedEditPositionFrom (last_assigned_index
, true); // Find the first non-assigned edit position
719 testPosition
= start
;
720 resultHint
= MaskedTextResultHint
.Unknown
;
722 if (endindex
== InvalidIndex
) {
723 // No more free edit positions.
724 testPosition
= edit_positions
.Length
;
725 resultHint
= MaskedTextResultHint
.UnavailableEditPosition
;
729 while (endindex
> index
) {
733 index_to_move
= FindEditPositionFrom (endindex
- 1, false);
734 char_to_assign
= edit_positions
[index_to_move
].input
;
736 if (char_to_assign
== char.MinValue
) {
737 edit_positions
[endindex
].input
= char_to_assign
;
739 if (!edit_positions
[endindex
].Match (char_to_assign
, out resultHint
, false)) {
740 testPosition
= endindex
;
744 endindex
= index_to_move
;
747 if (endindex
!= InvalidIndex
) {
748 edit_positions
[endindex
].Reset ();
754 private bool ReplaceInternal (string input
, int startPosition
, int endPosition
, out int testPosition
, out MaskedTextResultHint resultHint
, bool only_test
, bool dont_remove_at_end
)
756 EditPosition
[] edit_positions
;
757 resultHint
= MaskedTextResultHint
.Unknown
;
760 edit_positions
= ClonePositions ();
762 edit_positions
= this.edit_positions
;
766 throw new ArgumentNullException ("input");
768 if (endPosition
>= edit_positions
.Length
) {
769 testPosition
= endPosition
;
770 resultHint
= MaskedTextResultHint
.PositionOutOfRange
;
774 if (startPosition
< 0) {
775 testPosition
= startPosition
;
776 resultHint
= MaskedTextResultHint
.PositionOutOfRange
;
780 if (startPosition
>= edit_positions
.Length
) {
781 testPosition
= startPosition
;
782 resultHint
= MaskedTextResultHint
.PositionOutOfRange
;
786 if (startPosition
> endPosition
) {
787 testPosition
= startPosition
;
788 resultHint
= MaskedTextResultHint
.PositionOutOfRange
;
793 if (input
.Length
== 0) {
794 return RemoveAtInternal (startPosition
, endPosition
, out testPosition
, out resultHint
, only_test
);
797 int previous_position
= startPosition
;
798 int current_position
= previous_position
;
799 MaskedTextResultHint tmpResult
= MaskedTextResultHint
.Unknown
;
800 testPosition
= InvalidIndex
;
802 for (int i
= 0; i
< input
.Length
; i
++) {
803 char current_input
= input
[i
];
805 current_position
= previous_position
;
807 if (VerifyEscapeChar (current_input
, current_position
)) {
808 if (edit_positions
[current_position
].FilledIn
&&
809 edit_positions
[current_position
].Editable
&&
810 (current_input
== ' ' && ResetOnSpace
) || (current_input
== PromptChar
&& ResetOnPrompt
)) {
811 edit_positions
[current_position
].Reset ();
812 tmpResult
= MaskedTextResultHint
.SideEffect
;
814 tmpResult
= MaskedTextResultHint
.CharacterEscaped
;
816 } else if (current_position
< edit_positions
.Length
&& !edit_positions
[current_position
].Editable
&& FindAssignedEditPositionInRange (current_position
, endPosition
, true) == InvalidIndex
) {
817 // If replacing over a literal, the replacement character is INSERTED at the next
818 // available edit position. Weird, huh?
819 current_position
= FindEditPositionFrom (current_position
, true);
820 if (current_position
== InvalidIndex
) {
821 resultHint
= MaskedTextResultHint
.UnavailableEditPosition
;
822 testPosition
= edit_positions
.Length
;
825 if (!InsertAtInternal (current_input
.ToString (), current_position
, out testPosition
, out tmpResult
, only_test
)) {
826 resultHint
= tmpResult
;
831 current_position
= FindEditPositionFrom (current_position
, true);
833 if (current_position
== InvalidIndex
) {
834 testPosition
= edit_positions
.Length
;
835 resultHint
= MaskedTextResultHint
.UnavailableEditPosition
;
839 if (!IsValidInputChar (current_input
)) {
840 testPosition
= current_position
;
841 resultHint
= MaskedTextResultHint
.InvalidInput
;
845 if (!ReplaceInternal (edit_positions
, current_input
, current_position
, out testPosition
, out tmpResult
, false)) {
846 resultHint
= tmpResult
;
851 if (tmpResult
> resultHint
) {
852 resultHint
= tmpResult
;
855 previous_position
= current_position
+ 1;
858 testPosition
= current_position
;
861 if (!dont_remove_at_end
&& previous_position
<= endPosition
) {
862 if (!RemoveAtInternal (previous_position
, endPosition
, out tmpPosition
, out tmpResult
, only_test
)) {
863 testPosition
= tmpPosition
;
864 resultHint
= tmpResult
;
868 if (tmpResult
== MaskedTextResultHint
.Success
&& resultHint
< MaskedTextResultHint
.SideEffect
) {
869 resultHint
= MaskedTextResultHint
.SideEffect
;
875 private bool ReplaceInternal (EditPosition
[] edit_positions
, char input
, int position
, out int testPosition
, out MaskedTextResultHint resultHint
, bool only_test
)
877 testPosition
= position
;
879 if (!IsValidInputChar (input
)) {
880 resultHint
= MaskedTextResultHint
.InvalidInput
;
884 if (VerifyEscapeChar (input
, position
)) {
885 if (edit_positions
[position
].FilledIn
&& edit_positions
[position
].Editable
&& (input
== ' ' && ResetOnSpace
) || (input
== PromptChar
&& ResetOnPrompt
)) {
886 edit_positions
[position
].Reset ();
887 resultHint
= MaskedTextResultHint
.SideEffect
;
889 resultHint
= MaskedTextResultHint
.CharacterEscaped
;
891 testPosition
= position
;
895 if (!edit_positions
[position
].Editable
) {
896 resultHint
= MaskedTextResultHint
.NonEditPosition
;
900 bool is_filled
= edit_positions
[position
].FilledIn
;
901 if (is_filled
&& edit_positions
[position
].input
== input
) {
902 if (VerifyEscapeChar (input
, position
)) {
903 resultHint
= MaskedTextResultHint
.CharacterEscaped
;
905 resultHint
= MaskedTextResultHint
.NoEffect
;
907 } else if (input
== ' ' && this.ResetOnSpace
) {
909 resultHint
= MaskedTextResultHint
.SideEffect
;
910 edit_positions
[position
].Reset ();
912 resultHint
= MaskedTextResultHint
.CharacterEscaped
;
915 } else if (VerifyEscapeChar (input
, position
)) {
916 resultHint
= MaskedTextResultHint
.SideEffect
;
918 resultHint
= MaskedTextResultHint
.Success
;
920 MaskedTextResultHint tmpResult
;
921 if (!edit_positions
[position
].Match (input
, out tmpResult
, false)) {
922 resultHint
= tmpResult
;
929 private bool RemoveAtInternal (int startPosition
, int endPosition
, out int testPosition
, out MaskedTextResultHint resultHint
, bool only_testing
)
931 EditPosition
[] edit_positions
;
933 resultHint
= MaskedTextResultHint
.Unknown
;
936 edit_positions
= ClonePositions ();
938 edit_positions
= this.edit_positions
;
941 if (endPosition
< 0 || endPosition
>= edit_positions
.Length
) {
942 testPosition
= endPosition
;
943 resultHint
= MaskedTextResultHint
.PositionOutOfRange
;
947 if (startPosition
< 0 || startPosition
>= edit_positions
.Length
) {
948 testPosition
= startPosition
;
949 resultHint
= MaskedTextResultHint
.PositionOutOfRange
;
954 if (startPosition
> endPosition
) {
955 testPosition
= startPosition
;
956 resultHint
= MaskedTextResultHint
.PositionOutOfRange
;
959 int edit_positions_in_range
= 0;
960 for (int i
= startPosition
; i
<= endPosition
; i
++) {
961 if (edit_positions
[i
].Editable
) {
962 edit_positions_in_range
++;
966 if (edit_positions_in_range
== 0) {
967 testPosition
= startPosition
;
968 resultHint
= MaskedTextResultHint
.NoEffect
;
972 int current_edit_position
= FindEditPositionFrom (startPosition
, true);
973 while (current_edit_position
!= InvalidIndex
) {
974 // Find the edit position that will reach the current position.
975 int next_index
= FindEditPositionFrom (current_edit_position
+ 1, true);
976 for (int j
= 1; j
< edit_positions_in_range
&& next_index
!= InvalidIndex
; j
++) {
977 next_index
= FindEditPositionFrom (next_index
+ 1, true);
980 if (next_index
== InvalidIndex
) {
981 if (edit_positions
[current_edit_position
].FilledIn
) {
982 edit_positions
[current_edit_position
].Reset ();
983 resultHint
= MaskedTextResultHint
.Success
;
985 if (resultHint
< MaskedTextResultHint
.NoEffect
) {
986 resultHint
= MaskedTextResultHint
.NoEffect
;
990 if (!edit_positions
[next_index
].FilledIn
) {
991 if (edit_positions
[current_edit_position
].FilledIn
) {
992 edit_positions
[current_edit_position
].Reset ();
993 resultHint
= MaskedTextResultHint
.Success
;
995 if (resultHint
< MaskedTextResultHint
.NoEffect
) {
996 resultHint
= MaskedTextResultHint
.NoEffect
;
1000 MaskedTextResultHint tmpResult
= MaskedTextResultHint
.Unknown
;
1001 if (edit_positions
[current_edit_position
].FilledIn
) {
1002 resultHint
= MaskedTextResultHint
.Success
;
1003 } else if (resultHint
< MaskedTextResultHint
.SideEffect
) {
1004 resultHint
= MaskedTextResultHint
.SideEffect
;
1006 if (!edit_positions
[current_edit_position
].Match (edit_positions
[next_index
].input
, out tmpResult
, false)) {
1007 resultHint
= tmpResult
;
1008 testPosition
= current_edit_position
;
1012 edit_positions
[next_index
].Reset ();
1014 current_edit_position
= FindEditPositionFrom (current_edit_position
+ 1, true);
1017 if (resultHint
== MaskedTextResultHint
.Unknown
) {
1018 resultHint
= MaskedTextResultHint
.NoEffect
;
1021 testPosition
= startPosition
;
1026 private bool InsertAtInternal (string str_input
, int position
, out int testPosition
, out MaskedTextResultHint resultHint
, bool only_testing
)
1028 EditPosition
[] edit_positions
;
1030 resultHint
= MaskedTextResultHint
.Unknown
;
1033 edit_positions
= ClonePositions ();
1035 edit_positions
= this.edit_positions
;
1038 if (position
< 0 || position
>= edit_positions
.Length
) {
1040 resultHint
= MaskedTextResultHint
.PositionOutOfRange
;
1044 if (!IsInsertableString (str_input
, position
, out testPosition
, out resultHint
)) {
1048 resultHint
= MaskedTextResultHint
.Unknown
;
1050 int next_position
= position
;
1051 for (int i
= 0; i
< str_input
.Length
; i
++) {
1052 char input
= str_input
[i
];
1053 int index
= FindEditPositionFrom (next_position
, true); // Find the first edit position (here the input will go)
1054 int endindex
= FindUnassignedEditPositionFrom (next_position
, true); // Find the first non-assigned edit position
1055 bool escaped
= false;
1057 if (VerifyEscapeChar (input
, next_position
)) {
1060 if (input
.ToString () == edit_positions
[next_position
].Text
) {
1061 if (FindAssignedEditPositionInRange (0, next_position
- 1, true) != InvalidIndex
&& endindex
== InvalidIndex
) {
1062 resultHint
= MaskedTextResultHint
.UnavailableEditPosition
;
1063 testPosition
= edit_positions
.Length
;
1066 resultHint
= MaskedTextResultHint
.CharacterEscaped
;
1067 testPosition
= next_position
;
1073 if (!escaped
&& index
== InvalidIndex
) {
1074 // No edit positions left at all in the string
1075 testPosition
= edit_positions
.Length
;
1076 resultHint
= MaskedTextResultHint
.UnavailableEditPosition
;
1080 if (index
== InvalidIndex
) {
1081 index
= next_position
;
1084 bool was_filled
= edit_positions
[index
].FilledIn
;
1085 bool shift
= was_filled
;
1087 if (!ShiftPositionsRight (edit_positions
, index
, out testPosition
, out resultHint
)) {
1092 testPosition
= index
;
1095 resultHint
= MaskedTextResultHint
.Success
;
1096 } else if (!edit_positions
[index
].Editable
&& input
.ToString () == edit_positions
[index
].Text
) {
1097 resultHint
= MaskedTextResultHint
.CharacterEscaped
;
1098 testPosition
= next_position
;
1100 int first_edit_position
= FindEditPositionFrom (index
, true);
1101 if (first_edit_position
== InvalidIndex
) {
1102 resultHint
= MaskedTextResultHint
.UnavailableEditPosition
;
1103 testPosition
= edit_positions
.Length
;
1107 resultHint
= MaskedTextResultHint
.CharacterEscaped
;
1108 if (input
.ToString () == edit_positions
[next_position
].Text
) {
1109 testPosition
= next_position
;
1113 MaskedTextResultHint tmpResult
;
1114 if (!edit_positions
[index
].Match (input
, out tmpResult
, false)) {
1115 resultHint
= tmpResult
;
1118 if (resultHint
< tmpResult
) {
1119 resultHint
= tmpResult
;
1122 next_position
= index
+ 1;
1129 #region Public constructors
1130 static MaskedTextProvider()
1134 public MaskedTextProvider(string mask
)
1135 : this (mask
, null, true, default_prompt_char
, default_password_char
, false)
1139 public MaskedTextProvider (string mask
, bool restrictToAscii
)
1140 : this (mask
, null, true, default_prompt_char
, default_password_char
, restrictToAscii
)
1144 public MaskedTextProvider (string mask
, CultureInfo culture
)
1145 : this (mask
, culture
, true, default_prompt_char
, default_password_char
, false)
1149 public MaskedTextProvider (string mask
, char passwordChar
, bool allowPromptAsInput
)
1150 : this (mask
, null, allowPromptAsInput
, default_prompt_char
, passwordChar
, false)
1154 public MaskedTextProvider (string mask
, CultureInfo culture
, bool restrictToAscii
)
1155 : this (mask
, culture
, true, default_prompt_char
, default_password_char
, restrictToAscii
)
1159 public MaskedTextProvider(string mask
, CultureInfo culture
, char passwordChar
, bool allowPromptAsInput
)
1160 : this (mask
, culture
, allowPromptAsInput
, default_prompt_char
, passwordChar
, false)
1164 public MaskedTextProvider(string mask
, CultureInfo culture
, bool allowPromptAsInput
, char promptChar
, char passwordChar
, bool restrictToAscii
)
1168 if (culture
== null)
1169 this.culture
= Threading
.Thread
.CurrentThread
.CurrentCulture
;
1171 this.culture
= culture
;
1173 this.allow_prompt_as_input
= allowPromptAsInput
;
1175 this.PromptChar
= promptChar
;
1176 this.PasswordChar
= passwordChar
;
1177 this.ascii_only
= restrictToAscii
;
1179 include_literals
= true;
1180 reset_on_prompt
= true;
1181 reset_on_space
= true;
1182 skip_literals
= true;
1186 #region Public methods
1187 public bool Add(char input
)
1190 MaskedTextResultHint resultHint
;
1191 return Add (input
, out testPosition
, out resultHint
);
1194 public bool Add (string input
)
1197 MaskedTextResultHint resultHint
;
1198 return Add (input
, out testPosition
, out resultHint
);
1201 public bool Add (char input
, out int testPosition
, out MaskedTextResultHint resultHint
)
1203 return AddInternal (input
, out testPosition
, out resultHint
, true, false);
1206 public bool Add (string input
, out int testPosition
, out MaskedTextResultHint resultHint
)
1210 result
= AddInternal (input
, out testPosition
, out resultHint
, true);
1213 result
= AddInternal (input
, out testPosition
, out resultHint
, false);
1219 public void Clear ()
1221 MaskedTextResultHint resultHint
;
1222 Clear (out resultHint
);
1225 public void Clear (out MaskedTextResultHint resultHint
)
1227 resultHint
= MaskedTextResultHint
.NoEffect
;
1228 for (int i
= 0; i
< edit_positions
.Length
; i
++) {
1229 if (edit_positions
[i
].Editable
&& edit_positions
[i
].FilledIn
) {
1230 edit_positions
[i
].Reset ();
1231 resultHint
= MaskedTextResultHint
.Success
;
1236 public object Clone ()
1238 MaskedTextProvider result
= new MaskedTextProvider (mask
);
1240 result
.allow_prompt_as_input
= allow_prompt_as_input
;
1241 result
.ascii_only
= ascii_only
;
1242 result
.culture
= culture
;
1243 result
.edit_positions
= ClonePositions ();
1244 result
.include_literals
= include_literals
;
1245 result
.include_prompt
= include_prompt
;
1246 result
.is_password
= is_password
;
1248 result
.password_char
= password_char
;
1249 result
.prompt_char
= prompt_char
;
1250 result
.reset_on_prompt
= reset_on_prompt
;
1251 result
.reset_on_space
= reset_on_space
;
1252 result
.skip_literals
= skip_literals
;
1257 public int FindAssignedEditPositionFrom (int position
, bool direction
)
1260 return FindAssignedEditPositionInRange (position
, edit_positions
.Length
- 1, direction
);
1262 return FindAssignedEditPositionInRange (0, position
, direction
);
1266 public int FindAssignedEditPositionInRange (int startPosition
, int endPosition
, bool direction
)
1271 if (startPosition
< 0)
1273 if (endPosition
>= edit_positions
.Length
)
1274 endPosition
= edit_positions
.Length
- 1;
1276 if (startPosition
> endPosition
)
1277 return InvalidIndex
;
1279 step
= direction
? 1 : -1;
1280 start
= direction
? startPosition
: endPosition
;
1281 end
= (direction
? endPosition
: startPosition
) + step
;
1283 for (int i
= start
; i
!= end
; i
+=step
) {
1284 if (edit_positions
[i
].Editable
&& edit_positions
[i
].FilledIn
)
1287 return InvalidIndex
;
1290 public int FindEditPositionFrom (int position
, bool direction
)
1293 return FindEditPositionInRange (position
, edit_positions
.Length
- 1, direction
);
1295 return FindEditPositionInRange (0, position
, direction
);
1299 public int FindEditPositionInRange (int startPosition
, int endPosition
, bool direction
)
1304 if (startPosition
< 0)
1306 if (endPosition
>= edit_positions
.Length
)
1307 endPosition
= edit_positions
.Length
- 1;
1309 if (startPosition
> endPosition
)
1310 return InvalidIndex
;
1312 step
= direction
? 1 : -1;
1313 start
= direction
? startPosition
: endPosition
;
1314 end
= (direction
? endPosition
: startPosition
) + step
;
1316 for (int i
= start
; i
!= end
; i
+= step
) {
1317 if (edit_positions
[i
].Editable
)
1320 return InvalidIndex
;
1323 public int FindNonEditPositionFrom (int position
, bool direction
)
1326 return FindNonEditPositionInRange (position
, edit_positions
.Length
- 1, direction
);
1328 return FindNonEditPositionInRange (0, position
, direction
);
1332 public int FindNonEditPositionInRange (int startPosition
, int endPosition
, bool direction
)
1337 if (startPosition
< 0)
1339 if (endPosition
>= edit_positions
.Length
)
1340 endPosition
= edit_positions
.Length
- 1;
1342 if (startPosition
> endPosition
)
1343 return InvalidIndex
;
1345 step
= direction
? 1 : -1;
1346 start
= direction
? startPosition
: endPosition
;
1347 end
= (direction
? endPosition
: startPosition
) + step
;
1349 for (int i
= start
; i
!= end
; i
+= step
) {
1350 if (!edit_positions
[i
].Editable
)
1353 return InvalidIndex
;
1356 public int FindUnassignedEditPositionFrom (int position
, bool direction
)
1359 return FindUnassignedEditPositionInRange (position
, edit_positions
.Length
- 1, direction
);
1361 return FindUnassignedEditPositionInRange (0, position
, direction
);
1365 public int FindUnassignedEditPositionInRange (int startPosition
, int endPosition
, bool direction
)
1370 if (startPosition
< 0)
1372 if (endPosition
>= edit_positions
.Length
)
1373 endPosition
= edit_positions
.Length
- 1;
1375 if (startPosition
> endPosition
)
1376 return InvalidIndex
;
1378 step
= direction
? 1 : -1;
1379 start
= direction
? startPosition
: endPosition
;
1380 end
= (direction
? endPosition
: startPosition
) + step
;
1382 for (int i
= start
; i
!= end
; i
+= step
) {
1383 if (edit_positions
[i
].Editable
&& !edit_positions
[i
].FilledIn
)
1386 return InvalidIndex
;
1389 public static bool GetOperationResultFromHint (MaskedTextResultHint hint
)
1391 return (hint
== MaskedTextResultHint
.CharacterEscaped
||
1392 hint
== MaskedTextResultHint
.NoEffect
||
1393 hint
== MaskedTextResultHint
.SideEffect
||
1394 hint
== MaskedTextResultHint
.Success
);
1397 public bool InsertAt (char input
, int position
)
1400 MaskedTextResultHint resultHint
;
1401 return InsertAt (input
, position
, out testPosition
, out resultHint
);
1404 public bool InsertAt (string input
, int position
)
1407 MaskedTextResultHint resultHint
;
1408 return InsertAt (input
, position
, out testPosition
, out resultHint
);
1411 public bool InsertAt (char input
, int position
, out int testPosition
, out MaskedTextResultHint resultHint
)
1413 return InsertAt (input
.ToString (), position
, out testPosition
, out resultHint
);
1416 public bool InsertAt (string input
, int position
, out int testPosition
, out MaskedTextResultHint resultHint
)
1419 throw new ArgumentNullException ("input");
1421 if (position
>= edit_positions
.Length
) {
1422 testPosition
= position
;
1423 resultHint
= MaskedTextResultHint
.PositionOutOfRange
;
1427 if (input
== string.Empty
) {
1428 testPosition
= position
;
1429 resultHint
= MaskedTextResultHint
.NoEffect
;
1435 result
= InsertAtInternal (input
, position
, out testPosition
, out resultHint
, true);
1438 result
= InsertAtInternal (input
, position
, out testPosition
, out resultHint
, false);
1444 public bool IsAvailablePosition (int position
)
1446 if (position
< 0 || position
>= edit_positions
.Length
)
1449 return edit_positions
[position
].Editable
&& !edit_positions
[position
].FilledIn
;
1452 public bool IsEditPosition (int position
)
1454 if (position
< 0 || position
>= edit_positions
.Length
)
1457 return edit_positions
[position
].Editable
;
1460 public static bool IsValidInputChar (char c
)
1462 return char.IsLetterOrDigit (c
) || char.IsPunctuation (c
) || char.IsSymbol (c
) || c
== ' ';
1465 public static bool IsValidMaskChar (char c
)
1467 return char.IsLetterOrDigit (c
) || char.IsPunctuation (c
) || char.IsSymbol (c
) || c
== ' ';
1470 public static bool IsValidPasswordChar (char c
)
1472 return char.IsLetterOrDigit (c
) || char.IsPunctuation (c
) || char.IsSymbol (c
) || c
== ' ' || c
== char.MinValue
;
1475 public bool Remove ()
1478 MaskedTextResultHint resultHint
;
1479 return Remove (out testPosition
, out resultHint
);
1482 public bool Remove (out int testPosition
, out MaskedTextResultHint resultHint
)
1484 if (LastAssignedPosition
== InvalidIndex
) {
1485 resultHint
= MaskedTextResultHint
.NoEffect
;
1490 testPosition
= LastAssignedPosition
;
1491 resultHint
= MaskedTextResultHint
.Success
;
1492 edit_positions
[LastAssignedPosition
].input
= char.MinValue
;
1497 public bool RemoveAt (int position
)
1499 return RemoveAt (position
, position
);
1502 public bool RemoveAt (int startPosition
, int endPosition
)
1505 MaskedTextResultHint resultHint
;
1506 return RemoveAt (startPosition
, endPosition
, out testPosition
, out resultHint
);
1509 public bool RemoveAt (int startPosition
, int endPosition
, out int testPosition
, out MaskedTextResultHint resultHint
)
1513 result
= RemoveAtInternal (startPosition
, endPosition
, out testPosition
, out resultHint
, true);
1516 result
= RemoveAtInternal (startPosition
, endPosition
, out testPosition
, out resultHint
, false);
1522 public bool Replace (char input
, int position
)
1525 MaskedTextResultHint resultHint
;
1526 return Replace (input
, position
, out testPosition
, out resultHint
);
1529 public bool Replace (string input
, int position
)
1532 MaskedTextResultHint resultHint
;
1533 return Replace (input
, position
, out testPosition
, out resultHint
);
1536 public bool Replace (char input
, int position
, out int testPosition
, out MaskedTextResultHint resultHint
)
1538 if (position
< 0 || position
>= edit_positions
.Length
) {
1539 testPosition
= position
;
1540 resultHint
= MaskedTextResultHint
.PositionOutOfRange
;
1544 if (VerifyEscapeChar (input
, position
)) {
1545 if (edit_positions
[position
].FilledIn
&& edit_positions
[position
].Editable
&& (input
== ' ' && ResetOnSpace
) || (input
== PromptChar
&& ResetOnPrompt
)) {
1546 edit_positions
[position
].Reset ();
1547 resultHint
= MaskedTextResultHint
.SideEffect
;
1549 resultHint
= MaskedTextResultHint
.CharacterEscaped
;
1551 testPosition
= position
;
1555 int current_edit_position
;
1556 current_edit_position
= FindEditPositionFrom (position
, true);
1558 if (current_edit_position
== InvalidIndex
) {
1559 testPosition
= position
;
1560 resultHint
= MaskedTextResultHint
.UnavailableEditPosition
;
1564 if (!IsValidInputChar (input
)) {
1565 testPosition
= current_edit_position
;
1566 resultHint
= MaskedTextResultHint
.InvalidInput
;
1570 return ReplaceInternal (edit_positions
, input
, current_edit_position
, out testPosition
, out resultHint
, false);
1573 public bool Replace (string input
, int position
, out int testPosition
, out MaskedTextResultHint resultHint
)
1576 throw new ArgumentNullException ("input");
1578 if (position
< 0 || position
>= edit_positions
.Length
) {
1579 testPosition
= position
;
1580 resultHint
= MaskedTextResultHint
.PositionOutOfRange
;
1584 if (input
.Length
== 0) {
1585 return RemoveAt (position
, position
, out testPosition
, out resultHint
);
1590 result
= ReplaceInternal (input
, position
, edit_positions
.Length
- 1, out testPosition
, out resultHint
, true, true);
1593 result
= ReplaceInternal (input
, position
, edit_positions
.Length
- 1, out testPosition
, out resultHint
, false, true);
1599 public bool Replace (char input
, int startPosition
, int endPosition
, out int testPosition
, out MaskedTextResultHint resultHint
)
1602 if (endPosition
>= edit_positions
.Length
) {
1603 testPosition
= endPosition
;
1604 resultHint
= MaskedTextResultHint
.PositionOutOfRange
;
1608 if (startPosition
< 0) {
1609 testPosition
= startPosition
;
1610 resultHint
= MaskedTextResultHint
.PositionOutOfRange
;
1614 if (startPosition
> endPosition
) {
1615 testPosition
= startPosition
;
1616 resultHint
= MaskedTextResultHint
.PositionOutOfRange
;
1620 if (startPosition
== endPosition
) {
1621 return ReplaceInternal (edit_positions
, input
, startPosition
, out testPosition
, out resultHint
, false);
1624 return Replace (input
.ToString (), startPosition
, endPosition
, out testPosition
, out resultHint
);
1627 public bool Replace (string input
, int startPosition
, int endPosition
, out int testPosition
, out MaskedTextResultHint resultHint
)
1631 result
= ReplaceInternal (input
, startPosition
, endPosition
, out testPosition
, out resultHint
, true, false);
1634 result
= ReplaceInternal (input
, startPosition
, endPosition
, out testPosition
, out resultHint
, false, false);
1639 public bool Set (string input
)
1642 MaskedTextResultHint resultHint
;
1643 return Set (input
, out testPosition
, out resultHint
);
1646 public bool Set (string input
, out int testPosition
, out MaskedTextResultHint resultHint
)
1650 if (input
== null) {
1651 throw new ArgumentNullException ("input");
1654 result
= VerifyStringInternal (input
, out testPosition
, out resultHint
, 0, true);
1657 result
= VerifyStringInternal (input
, out testPosition
, out resultHint
, 0, false);
1663 public string ToDisplayString ()
1665 return ToString (false, true, true, 0, Length
);
1668 public override string ToString ()
1670 return ToString (true, IncludePrompt
, IncludeLiterals
, 0, Length
);
1673 public string ToString (bool ignorePasswordChar
)
1675 return ToString (ignorePasswordChar
, IncludePrompt
, IncludeLiterals
, 0, Length
);
1678 public string ToString (bool includePrompt
, bool includeLiterals
)
1680 return ToString (true, includePrompt
, includeLiterals
, 0, Length
);
1683 public string ToString (int startPosition
, int length
)
1685 return ToString (true, IncludePrompt
, IncludeLiterals
, startPosition
, length
);
1688 public string ToString (bool ignorePasswordChar
, int startPosition
, int length
)
1690 return ToString (ignorePasswordChar
, IncludePrompt
, IncludeLiterals
, startPosition
, length
);
1693 public string ToString (bool includePrompt
, bool includeLiterals
, int startPosition
, int length
)
1695 return ToString (true, includePrompt
, includeLiterals
, startPosition
, length
);
1698 public string ToString (bool ignorePasswordChar
, bool includePrompt
, bool includeLiterals
, int startPosition
, int length
)
1700 if (startPosition
< 0)
1704 return string.Empty
;
1706 StringBuilder result
= new StringBuilder ();
1707 int start
= startPosition
;
1708 int end
= startPosition
+ length
- 1;
1710 if (end
>= edit_positions
.Length
) {
1711 end
= edit_positions
.Length
- 1;
1713 int last_assigned_position
= FindAssignedEditPositionInRange (start
, end
, false);
1715 // Find the last position in the mask to check for
1716 if (!includePrompt
) {
1717 int last_non_edit_position
;
1719 last_non_edit_position
= FindNonEditPositionInRange (start
, end
, false);
1721 if (includeLiterals
) {
1722 end
= last_assigned_position
> last_non_edit_position
? last_assigned_position
: last_non_edit_position
;
1724 end
= last_assigned_position
;
1728 for (int i
= start
; i
<= end
; i
++) {
1729 EditPosition ed
= edit_positions
[i
];
1731 if (ed
.Type
== EditType
.Literal
) {
1732 if (includeLiterals
) {
1733 result
.Append (ed
.Text
);
1735 } else if (ed
.Editable
) {
1737 if (!ed
.FilledIn
) { // Nothing to hide or show.
1739 result
.Append (PromptChar
);
1741 result
.Append (" ");
1742 } else if (ignorePasswordChar
)
1743 result
.Append (ed
.Input
);
1745 result
.Append (PasswordChar
);
1746 } else if (!ed
.FilledIn
) {
1747 if (includePrompt
) {
1748 result
.Append (PromptChar
);
1749 } else if (includeLiterals
) {
1750 result
.Append (" ");
1751 } else if (last_assigned_position
!= InvalidIndex
&& last_assigned_position
> i
) {
1752 result
.Append (" ");
1755 result
.Append (ed
.Text
);
1758 if (includeLiterals
)
1759 result
.Append (ed
.Text
);
1763 return result
.ToString ();
1766 public bool VerifyChar (char input
, int position
, out MaskedTextResultHint hint
)
1768 return VerifyCharInternal (input
, position
, out hint
, true);
1771 public bool VerifyEscapeChar (char input
, int position
)
1773 if (position
>= edit_positions
.Length
|| position
< 0) {
1777 if (!edit_positions
[position
].Editable
) {
1779 return input
.ToString () == edit_positions
[position
].Text
;
1785 if (ResetOnSpace
&& input
== ' ') {
1787 } else if (ResetOnPrompt
&& input
== PromptChar
) {
1794 public bool VerifyString (string input
)
1797 MaskedTextResultHint resultHint
;
1798 return VerifyString (input
, out testPosition
, out resultHint
);
1801 public bool VerifyString (string input
, out int testPosition
, out MaskedTextResultHint resultHint
)
1803 if (input
== null || input
.Length
== 0) {
1805 resultHint
= MaskedTextResultHint
.NoEffect
;
1809 return VerifyStringInternal (input
, out testPosition
, out resultHint
, 0, true);
1813 #region Public properties
1814 public bool AllowPromptAsInput
{
1816 return allow_prompt_as_input
;
1820 public bool AsciiOnly
{
1826 public int AssignedEditPositionCount
{
1829 for (int i
= 0; i
< edit_positions
.Length
; i
++) {
1830 if (edit_positions
[i
].FilledIn
) {
1838 public int AvailableEditPositionCount
{
1841 foreach (EditPosition edit
in edit_positions
) {
1842 if (!edit
.FilledIn
&& edit
.Editable
) {
1850 public CultureInfo Culture
{
1856 public static char DefaultPasswordChar
{
1862 public int EditPositionCount
{
1865 foreach (EditPosition edit
in edit_positions
) {
1866 if (edit
.Editable
) {
1875 public IEnumerator EditPositions
{
1877 System
.Collections
.Generic
.List
<int> result
= new System
.Collections
.Generic
.List
<int> ();
1878 for (int i
= 0; i
< edit_positions
.Length
; i
++) {
1879 if (edit_positions
[i
].Editable
) {
1883 return result
.GetEnumerator ();
1887 public bool IncludeLiterals
{
1889 return include_literals
;
1892 include_literals
= value;
1896 public bool IncludePrompt
{
1898 return include_prompt
;
1901 include_prompt
= value;
1905 public static int InvalidIndex
{
1911 public bool IsPassword
{
1913 return password_char
!= char.MinValue
;
1916 password_char
= value ? DefaultPasswordChar
: char.MinValue
;
1920 public char this [int index
] {
1922 if (index
< 0 || index
>= Length
) {
1923 throw new IndexOutOfRangeException (index
.ToString ());
1926 return ToString (true, true, true, 0, edit_positions
.Length
) [index
];
1930 public int LastAssignedPosition
{
1932 return FindAssignedEditPositionFrom (edit_positions
.Length
- 1, false);
1939 for (int i
= 0; i
< edit_positions
.Length
; i
++) {
1940 if (edit_positions
[i
].Visible
)
1947 public string Mask
{
1953 public bool MaskCompleted
{
1955 for (int i
= 0; i
< edit_positions
.Length
; i
++)
1956 if (edit_positions
[i
].Required
&& !edit_positions
[i
].FilledIn
)
1962 public bool MaskFull
{
1964 for (int i
= 0; i
< edit_positions
.Length
; i
++)
1965 if (edit_positions
[i
].Editable
&& !edit_positions
[i
].FilledIn
)
1971 public char PasswordChar
{
1973 return password_char
;
1976 password_char
= value;
1980 public char PromptChar
{
1985 prompt_char
= value;
1989 public bool ResetOnPrompt
{
1991 return reset_on_prompt
;
1994 reset_on_prompt
= value;
1998 public bool ResetOnSpace
{
2000 return reset_on_space
;
2003 reset_on_space
= value;
2007 public bool SkipLiterals
{
2009 return skip_literals
;
2012 skip_literals
= value;