home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 1999 February / DPPCPRO0299.ISO / February / Delphi / Runimage / DELPHI20 / SOURCE / VCL / MASK.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-06-08  |  38.3 KB  |  1,446 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Delphi Visual Component Library                 }
  5. {                                                       }
  6. {       Copyright (c) 1995,96 Borland International     }
  7. {                                                       }
  8. {*******************************************************}
  9.  
  10. unit Mask;
  11.  
  12. {$R-}
  13.  
  14. interface
  15.  
  16. uses Windows, SysUtils, Classes, StdCtrls, Controls, Messages,
  17.   Forms, Graphics, Menus;
  18.  
  19. const
  20.   DefaultBlank: Char = '_';
  21.   MaskFieldSeparator: Char = ';';
  22.   MaskNoSave: Char = '0';
  23.  
  24.   mDirReverse = '!';         { removes leading blanks if true, else trailing blanks}
  25.   mDirUpperCase = '>';       { all chars that follow to upper case }
  26.   mDirLowerCase = '<';       { all chars that follow to lower case }
  27.                              { '<>' means remove casing directive }
  28.   mDirLiteral = '\';         { char that immediately follows is a literal }
  29.  
  30.   mMskAlpha = 'L';           { in US = A-Z,a-z }
  31.   mMskAlphaOpt = 'l';
  32.   mMskAlphaNum = 'A';        { in US = A-Z,a-z,0-9 }
  33.   mMskAlphaNumOpt  = 'a';
  34.   mMskAscii = 'C';           { any character}
  35.   mMskAsciiOpt = 'c';
  36.   mMskNumeric = '0';         { 0-9, no plus or minus }
  37.   mMskNumericOpt = '9';
  38.   mMskNumSymOpt = '#';       { 0-9, plus and minus }
  39.  
  40.    { intl literals }
  41.   mMskTimeSeparator = ':';
  42.   mMskDateSeparator = '/';
  43.  
  44. type
  45.  
  46.   TMaskCharType = (mcNone, mcLiteral, mcIntlLiteral, mcDirective, mcMask,
  47.     mcMaskOpt, mcFieldSeparator, mcField);
  48.   TMaskDirectives = set of (mdReverseDir, mdUpperCase, mdLowerCase,
  49.     mdLiteralChar);
  50.  
  51. type
  52. { Exception class }
  53.   EDBEditError = class(Exception);
  54.  
  55.   TMaskedState = set of (msMasked, msReEnter, msDBSetText);
  56.  
  57. { TCustomMaskEdit }
  58.  
  59.   TCustomMaskEdit = class(TCustomEdit)
  60.   private
  61.     FEditMask: string;
  62.     FMaskBlank: Char;
  63.     FMaxChars: Integer;
  64.     FMaskSave: Boolean;
  65.     FMaskState: TMaskedState;
  66.     FCaretPos: Integer;
  67.     FBtnDownX: Integer;
  68.     FOldValue: string;
  69.     function DoInputChar(var NewChar: Char; MaskOffset: Integer): Boolean;
  70.     function Validate(const Value: string; var Pos: Integer): Boolean;
  71.     function InputChar(var NewChar: Char; Offset: Integer): Boolean;
  72.     function DeleteSelection(var Value: string; Offset: Integer;
  73.       Len: Integer): Boolean;
  74.     function InputString(var Value: string; const NewValue: string;
  75.       Offset: Integer): Integer;
  76.     function AddEditFormat(const Value: string; Active: Boolean): string;
  77.     function RemoveEditFormat(const Value: string): string;
  78.     function FindLiteralChar (MaskOffset: Integer; InChar: Char): Integer;
  79.     function GetEditText: string;
  80.     function GetMasked: Boolean;
  81.     function GetText: string;
  82.     function GetMaxLength: Integer;
  83.     function CharKeys(var CharCode: Char): Boolean;
  84.     procedure SetEditText(const Value: string);
  85.     procedure SetEditMask(const Value: string);
  86.     procedure SetMaxLength(Value: Integer);
  87.     procedure SetText(const Value: string);
  88.     procedure DeleteKeys(CharCode: Word);
  89.     procedure HomeEndKeys(CharCode: Word; Shift: TShiftState);
  90.     procedure CursorInc(CursorPos: Integer; Incr: Integer);
  91.     procedure CursorDec(CursorPos: Integer);
  92.     procedure ArrowKeys(CharCode: Word; Shift: TShiftState);
  93.     procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
  94.     procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
  95.     procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
  96.     procedure WMCut(var Message: TMessage); message WM_CUT;
  97.     procedure WMPaste(var Message: TMessage); message WM_PASTE;
  98.     procedure CMEnter(var Message: TCMEnter); message CM_ENTER;
  99.     procedure CMExit(var Message: TCMExit);   message CM_EXIT;
  100.     procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
  101.     procedure CMWantSpecialKey(var Message: TCMWantSpecialKey); message CM_WANTSPECIALKEY;
  102.   protected
  103.     procedure ReformatText(const NewMask: string);
  104.     procedure GetSel(var SelStart: Integer; var SelStop: Integer);
  105.     procedure SetSel(SelStart: Integer; SelStop: Integer);
  106.     procedure SetCursor(Pos: Integer);
  107.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  108.     procedure KeyUp(var Key: Word; Shift: TShiftState); override;
  109.     procedure KeyPress(var Key: Char); override;
  110.     function EditCanModify: Boolean; virtual;
  111.     procedure Reset; virtual;
  112.     function GetFirstEditChar: Integer;
  113.     function GetLastEditChar: Integer;
  114.     function GetNextEditChar(Offset: Integer): Integer;
  115.     function GetPriorEditChar(Offset: Integer): Integer;
  116.     function GetMaxChars: Integer;
  117.     procedure ValidateError;
  118.     procedure CheckCursor;
  119.     property EditMask: string read FEditMask write SetEditMask;
  120.     property MaskState: TMaskedState read FMaskState write FMaskState;
  121.     property MaxLength: Integer read GetMaxLength write SetMaxLength;
  122.   public
  123.     constructor Create(AOwner: TComponent); override;
  124.     procedure ValidateEdit;
  125.     procedure Clear;
  126.     function GetTextLen: Integer;
  127.     property IsMasked: Boolean read GetMasked;
  128.     property EditText: string read GetEditText write SetEditText;
  129.     property Text: string read GetText write SetText;
  130.   end;
  131.  
  132. { TMaskEdit }
  133.  
  134.   TMaskEdit = class(TCustomMaskEdit)
  135.   published
  136.     property AutoSelect;
  137.     property AutoSize;
  138.     property BorderStyle;
  139.     property CharCase;
  140.     property Color;
  141.     property Ctl3D;
  142.     property DragCursor;
  143.     property DragMode;
  144.     property Enabled;
  145.     property EditMask;
  146.     property Font;
  147.     property MaxLength;
  148.     property ParentColor;
  149.     property ParentCtl3D;
  150.     property ParentFont;
  151.     property ParentShowHint;
  152.     property PasswordChar;
  153.     property PopupMenu;
  154.     property ReadOnly;
  155.     property ShowHint;
  156.     property TabOrder;
  157.     property TabStop;
  158.     property Text;
  159.     property Visible;
  160.     property OnChange;
  161.     property OnClick;
  162.     property OnDblClick;
  163.     property OnDragDrop;
  164.     property OnDragOver;
  165.     property OnEndDrag;
  166.     property OnEnter;
  167.     property OnExit;
  168.     property OnKeyDown;
  169.     property OnKeyPress;
  170.     property OnKeyUp;
  171.     property OnMouseDown;
  172.     property OnMouseMove;
  173.     property OnMouseUp;
  174.     property OnStartDrag;
  175.   end;
  176.  
  177. function FormatMaskText(const EditMask: string; const Value: string): string;
  178. function MaskGetMaskSave(const EditMask: string): Boolean;
  179. function MaskGetMaskBlank(const EditMask: string): Char;
  180. function MaskGetFldSeparator(const EditMask: string): Integer;
  181.  
  182.  
  183. implementation
  184.  
  185. uses Clipbrd, Consts;
  186.  
  187. { Mask utility routines }
  188.  
  189. function MaskGetCharType(const EditMask: string; MaskOffset: Integer): TMaskCharType;
  190. var
  191.   MaskChar: Char;
  192. begin
  193.   Result := mcLiteral;
  194.   MaskChar := #0;
  195.   if MaskOffset <= Length(EditMask) then
  196.     MaskChar := EditMask[MaskOffset];
  197.   if MaskOffset > Length(EditMask) then
  198.     Result := mcNone
  199.  
  200.   else if (MaskOffset > 1) and (EditMask[MaskOffset - 1] = mDirLiteral) and
  201.       not ((MaskOffset > 2) and (EditMask[MaskOffset - 2] = mDirLiteral)) then
  202.     Result := mcLiteral
  203.  
  204.   else if (MaskChar = MaskFieldSeparator) and
  205.          (Length(EditMask) >= 4) and
  206.          (MaskOffset > Length(EditMask) - 4) then
  207.     Result := mcFieldSeparator
  208.  
  209.   else if (Length(EditMask) >= 4) and
  210.          (MaskOffset > (Length(EditMask) - 4)) and
  211.          (EditMask[MaskOffset - 1] = MaskFieldSeparator) and
  212.           not ((MaskOffset > 2) and
  213.                     (EditMask[MaskOffset - 2] = mDirLiteral)) then
  214.     Result := mcField
  215.  
  216.   else if MaskChar in [mMskTimeSeparator, mMskDateSeparator] then
  217.     Result := mcIntlLiteral
  218.  
  219.   else if MaskChar in [mDirReverse, mDirUpperCase, mDirLowerCase,
  220.       mDirLiteral] then
  221.     Result := mcDirective
  222.  
  223.   else if MaskChar in [mMskAlphaOpt, mMskAlphaNumOpt, mMskAsciiOpt,
  224.       mMskNumSymOpt, mMskNumericOpt] then
  225.     Result := mcMaskOpt
  226.  
  227.   else if MaskChar in [mMskAlpha, mMskAlphaNum, mMskAscii, mMskNumeric] then
  228.     Result := mcMask;
  229. end;
  230.  
  231. function MaskGetCurrentDirectives(const EditMask: string;
  232.   MaskOffset: Integer): TMaskDirectives;
  233. var
  234.   I: Integer;
  235.   MaskChar: Char;
  236. begin
  237.   Result := [];
  238.   for I := 1 to Length(EditMask) do
  239.   begin
  240.     MaskChar := EditMask[I];
  241.     if (MaskChar = mDirReverse) then
  242.       Include(Result, mdReverseDir)
  243.     else if (MaskChar = mDirUpperCase) and (I < MaskOffset) then
  244.     begin
  245.       Exclude(Result, mdLowerCase);
  246.       if not ((I > 1) and (EditMask[I-1] = mDirLowerCase)) then
  247.         Include(Result, mdUpperCase);
  248.     end
  249.     else if (MaskChar = mDirLowerCase) and (I < MaskOffset) then
  250.     begin
  251.       Exclude(Result, mdUpperCase);
  252.       Include(Result, mdLowerCase);
  253.     end;
  254.   end;
  255.   if MaskGetCharType(EditMask, MaskOffset) = mcLiteral then
  256.     Include(Result, mdLiteralChar);
  257. end;
  258.  
  259. function MaskIntlLiteralToChar(IChar: Char): Char;
  260. begin
  261.   Result := IChar;
  262.   case IChar of
  263.     mMskTimeSeparator: Result := TimeSeparator;
  264.     mMskDateSeparator: Result := DateSeparator;
  265.   end;
  266. end;
  267.  
  268. function MaskDoFormatText(const EditMask: string; const Value: string;
  269.   Blank: Char): string;
  270. var
  271.   I: Integer;
  272.   Offset, MaskOffset: Integer;
  273.   CType: TMaskCharType;
  274.   Dir: TMaskDirectives;
  275. begin
  276.   Result := Value;
  277.   Dir := MaskGetCurrentDirectives(EditMask, 1);
  278.  
  279.   if not (mdReverseDir in Dir) then
  280.   begin
  281.       { starting at the beginning, insert literal chars in the string
  282.         and add spaces on the end }
  283.     Offset := 1;
  284.     for MaskOffset := 1 to Length(EditMask) do
  285.     begin
  286.       CType := MaskGetCharType(EditMask, MaskOffset);
  287.  
  288.       if CType in [mcLiteral, mcIntlLiteral] then
  289.       begin
  290.         Result := Copy(Result, 1, Offset - 1) +
  291.           MaskIntlLiteralToChar(EditMask[MaskOffset]) +
  292.           Copy(Result, Offset, Length(Result) - Offset + 1);
  293.         Inc(Offset);
  294.       end
  295.       else if CType in [mcMask, mcMaskOpt] then
  296.       begin
  297.         if Offset > Length(Result) then
  298.           Result := Result + Blank;
  299.         Inc(Offset);
  300.       end;
  301.     end;
  302.   end
  303.   else
  304.   begin
  305.       { starting at the end, insert literal chars in the string
  306.         and add spaces at the beginning }
  307.     Offset := Length(Result);
  308.     for I := 0 to(Length(EditMask) - 1) do
  309.     begin
  310.       MaskOffset := Length(EditMask) - I;
  311.       CType := MaskGetCharType(EditMask, MaskOffset);
  312.       if CType in [mcLiteral, mcIntlLiteral] then
  313.       begin
  314.         Result := Copy(Result, 1, Offset) +
  315.                MaskIntlLiteralToChar(EditMask[MaskOffset]) +
  316.                Copy(Result, Offset + 1, Length(Result) - Offset);
  317.       end
  318.       else if CType in [mcMask, mcMaskOpt] then
  319.       begin
  320.         if Offset < 1 then
  321.           Result := Blank + Result
  322.         else
  323.           Dec(Offset);
  324.       end;
  325.     end;
  326.   end;
  327. end;
  328.  
  329. function MaskGetMaskSave(const EditMask: string): Boolean;
  330. var
  331.   I: Integer;
  332.   Sep1, Sep2: Integer;
  333. begin
  334.   Result := True;
  335.   if Length(EditMask) >= 4 then
  336.   begin
  337.     Sep1 := -1;
  338.     Sep2 := -1;
  339.     I := Length(EditMask);
  340.     while Sep2 < 0 do
  341.     begin
  342.       if (MaskGetCharType(EditMask, I) =  mcFieldSeparator) then
  343.       begin
  344.         if Sep1 < 0 then
  345.           Sep1 := I
  346.         else
  347.           Sep2 := I;
  348.       end;
  349.       Dec(I);
  350.       if (I <= 0) or(I < Length(EditMask) - 4) then
  351.         Break;
  352.     end;
  353.     if Sep2 < 0 then
  354.       Sep2 := Sep1;
  355.     if Sep2 <> Length(EditMask) then
  356.       Result := not (EditMask [Sep2 + 1] = MaskNoSave);
  357.   end;
  358. end;
  359.  
  360. function MaskGetMaskBlank(const EditMask: string): Char;
  361. begin
  362.   Result := DefaultBlank;
  363.   if Length(EditMask) >= 4 then
  364.   begin
  365.     if (MaskGetCharType(EditMask, Length(EditMask) - 1) =
  366.                                                   mcFieldSeparator) then
  367.     begin
  368.         {in order for blank specifier to be valid, there
  369.          must also be a save specifier }
  370.       if (MaskGetCharType(EditMask, Length(EditMask) - 2) =
  371.                                                   mcFieldSeparator) or
  372.         (MaskGetCharType(EditMask, Length(EditMask) - 3) =
  373.                                                   mcFieldSeparator) then
  374.       begin
  375.         Result := EditMask [Length(EditMask)];
  376.       end;
  377.     end;
  378.   end;
  379. end;
  380.  
  381. function MaskGetFldSeparator(const EditMask: String): Integer;
  382. var
  383.   I: Integer;
  384. begin
  385.   Result := -1;
  386.   if Length(EditMask) >= 4 then
  387.   begin
  388.     for I := (Length(EditMask) - 4) to Length(EditMask) do
  389.     begin
  390.       if (MaskGetCharType(EditMask, I) = mcFieldSeparator) then
  391.       begin
  392.         Result := I;
  393.         Exit;
  394.       end;
  395.     end;
  396.   end;
  397. end;
  398.  
  399. function MaskOffsetToOffset(const EditMask: String; MaskOffset: Integer): Integer;
  400. var
  401.   I: Integer;
  402.   CType: TMaskCharType;
  403. begin
  404.   Result := 0;
  405.   for I := 1 to MaskOffset do
  406.   begin
  407.     CType := MaskGetCharType(EditMask, I);
  408.     if not (CType in [mcDirective, mcField, mcFieldSeparator]) then
  409.       Inc(Result);
  410.   end;
  411. end;
  412.  
  413. function OffsetToMaskOffset(const EditMask: string; Offset: Integer): Integer;
  414. var
  415.   I: Integer;
  416.   Count: Integer;
  417.   MaxChars: Integer;
  418. begin
  419.   MaxChars  := MaskOffsetToOffset(EditMask, Length(EditMask));
  420.   if Offset > MaxChars then
  421.   begin
  422.     Result := -1;
  423.     Exit;
  424.   end;
  425.  
  426.   Result := 0;
  427.   Count := Offset;
  428.   for I := 1 to Length(EditMask) do
  429.   begin
  430.     Inc(Result);
  431.     if not (mcDirective = MaskGetCharType(EditMask, I)) then
  432.     begin
  433.       Dec(Count);
  434.       if Count < 0 then
  435.         Exit;
  436.     end;
  437.   end;
  438. end;
  439.  
  440. function IsLiteralChar(const EditMask: string; Offset: Integer): Boolean;
  441. var
  442.   MaskOffset: Integer;
  443.   CType: TMaskCharType;
  444. begin
  445.   Result := False;
  446.   MaskOffset := OffsetToMaskOffset(EditMask, Offset);
  447.   if MaskOffset >= 0 then
  448.   begin
  449.     CType := MaskGetCharType(EditMask, MaskOffset);
  450.     Result := CType in [mcLiteral, mcIntlLiteral];
  451.   end;
  452. end;
  453.  
  454. function PadSubField(const EditMask: String; const Value: string;
  455.   StartFld, StopFld, Len: Integer; Blank: Char): string;
  456. var
  457.   Dir: TMaskDirectives;
  458.   StartPad: Integer;
  459.   K: Integer;
  460. begin
  461.   if (StopFld - StartFld) < Len then
  462.   begin
  463.      { found literal at position J, now pad it }
  464.     Dir := MaskGetCurrentDirectives(EditMask, 1);
  465.     StartPad := StopFld - 1;
  466.     if mdReverseDir in Dir then
  467.       StartPad := StartFld - 1;
  468.     Result := Copy(Value, 1, StartPad);
  469.     for K := 1 to (Len - (StopFld - StartFld)) do
  470.       Result := Result + Blank;
  471.     Result := Result + Copy(Value, StartPad + 1, Length(Value));
  472.   end
  473.   else if (StopFld - StartFld) > Len then
  474.   begin
  475.     Dir := MaskGetCurrentDirectives(EditMask, 1);
  476.     if mdReverseDir in Dir then
  477.       Result := Copy(Value, 1, StartFld - 1) +
  478.         Copy(Value, StopFld - Len, Length(Value))
  479.     else
  480.       Result := Copy(Value, 1, StartFld + Len - 1) +
  481.         Copy(Value, StopFld, Length(Value));
  482.   end
  483.   else
  484.     Result := Value;
  485. end;
  486.  
  487. function PadInputLiterals(const EditMask: String; const Value: string;
  488.   Blank: Char): string;
  489. var
  490.   J: Integer;
  491.   LastLiteral, EndSubFld: Integer;
  492.   Offset, MaskOffset: Integer;
  493.   CType: TMaskCharType;
  494.   MaxChars: Integer;
  495. begin
  496.   LastLiteral := 0;
  497.  
  498.   Result := Value;
  499.   for MaskOffset := 1 to Length(EditMask) do
  500.   begin
  501.     CType := MaskGetCharType(EditMask, MaskOffset);
  502.     if CType in [mcLiteral, mcIntlLiteral] then
  503.     begin
  504.       Offset := MaskOffsetToOffset(EditMask, MaskOffset);
  505.       EndSubFld := Length(Result) + 1;
  506.       for J := LastLiteral + 1 to Length(Result) do
  507.       begin
  508.         if Result[J] = MaskIntlLiteralToChar(EditMask[MaskOffset]) then
  509.         begin
  510.           EndSubFld := J;
  511.           Break;
  512.         end;
  513.       end;
  514.        { we have found a subfield, ensure that it complies }
  515.       if EndSubFld > Length(Result) then
  516.         Result := Result + MaskIntlLiteralToChar(EditMask[MaskOffset]);
  517.       Result := PadSubField(EditMask, Result, LastLiteral + 1, EndSubFld,
  518.         Offset - (LastLiteral + 1), Blank);
  519.       LastLiteral := Offset;
  520.     end;
  521.   end;
  522.  
  523.     {ensure that the remainder complies, too }
  524.   MaxChars  := MaskOffsetToOffset(EditMask, Length(EditMask));
  525.   if Length (Result) <> MaxChars then
  526.     Result := PadSubField(EditMask, Result, LastLiteral + 1, Length (Result) + 1,
  527.       MaxChars - LastLiteral, Blank);
  528.  
  529.     { replace non-literal blanks with blank char }
  530.   for Offset := 1 to Length (Result) do
  531.   begin
  532.     if Result[Offset] = ' ' then
  533.     begin
  534.       if not IsLiteralChar(EditMask, Offset - 1) then
  535.         Result[Offset] := Blank;
  536.     end;
  537.   end;
  538. end;
  539.  
  540. function FormatMaskText(const EditMask: string; const Value: string ): string;
  541. begin
  542.   if MaskGetMaskSave(EditMask) then
  543.     Result := PadInputLiterals(EditMask, Value, ' ')
  544.   else
  545.     Result := MaskDoFormatText(EditMask, Value, ' ');
  546. end;
  547.  
  548.  
  549. { TCustomMaskEdit }
  550.  
  551. constructor TCustomMaskEdit.Create(AOwner: TComponent);
  552. begin
  553.   inherited Create(AOwner);
  554.   FMaskState := [];
  555.   FMaskBlank := DefaultBlank;
  556. end;
  557.  
  558. procedure TCustomMaskEdit.KeyDown(var Key: Word; Shift: TShiftState);
  559. begin
  560.   inherited KeyDown(Key, Shift);
  561.   if IsMasked and (Key <> 0) and not (ssAlt in Shift) then
  562.   begin
  563.     if (Key = VK_LEFT) or(Key = VK_RIGHT) then
  564.     begin
  565.       ArrowKeys(Key, Shift);
  566.       if not ((ssShift in Shift) or (ssCtrl in Shift)) then
  567.         Key := 0;
  568.       Exit;
  569.     end
  570.     else if (Key = VK_UP) or(Key = VK_DOWN) then
  571.     begin
  572.       Key := 0;
  573.       Exit;
  574.     end
  575.     else if (Key = VK_HOME) or(Key = VK_END) then
  576.     begin
  577.       HomeEndKeys(Key, Shift);
  578.       Key := 0;
  579.       Exit;
  580.     end
  581.     else if ((Key = VK_DELETE) and ([ssShift, ssCtrl] * Shift = [])) or
  582.       (Key = VK_BACK) then
  583.     begin
  584.       if EditCanModify then
  585.         DeleteKeys(Key);
  586.       Key := 0;
  587.       Exit;
  588.     end;
  589.     CheckCursor;
  590.   end;
  591. end;
  592.  
  593. procedure TCustomMaskEdit.KeyUp(var Key: Word; Shift: TShiftState);
  594. begin
  595.   inherited KeyUp(Key, Shift);
  596.   if IsMasked and (Key <> 0) then
  597.   begin
  598.     if ((Key = VK_LEFT) or(Key = VK_RIGHT)) and (ssCtrl in Shift) then
  599.       CheckCursor;
  600.   end;
  601. end;
  602.  
  603. procedure TCustomMaskEdit.KeyPress(var Key: Char);
  604. begin
  605.   inherited KeyPress(Key);
  606.   if IsMasked and (Key <> #0) and not (Char(Key) in [^V, ^X, ^C]) then
  607.   begin
  608.     CharKeys(Key);
  609.     Key := #0;
  610.   end;
  611. end;
  612.  
  613. procedure TCustomMaskEdit.WMLButtonDown(var Message: TWMLButtonDown);
  614. begin
  615.   inherited;
  616.   FBtnDownX := Message.XPos;
  617. end;
  618.  
  619. procedure TCustomMaskEdit.WMLButtonUp(var Message: TWMLButtonUp);
  620. var
  621.   SelStart, SelStop : Integer;
  622. begin
  623.   inherited;
  624.   if (IsMasked) then
  625.   begin
  626.     GetSel(SelStart, SelStop);
  627.     FCaretPos := SelStart;
  628.     if (SelStart <> SelStop) and (Message.XPos > FBtnDownX) then
  629.       FCaretPos := SelStop;
  630.     CheckCursor;
  631.   end;
  632. end;
  633.  
  634. procedure TCustomMaskEdit.WMSetFocus(var Message: TWMSetFocus);
  635. begin
  636.   inherited;
  637.   if (IsMasked) then
  638.     CheckCursor;
  639. end;
  640.  
  641. procedure TCustomMaskEdit.SetEditText(const Value: string);
  642. begin
  643.   if GetEditText <> Value then
  644.   begin
  645.     SetTextBuf(PChar(Value));
  646.     CheckCursor;
  647.   end;
  648. end;
  649.  
  650. function TCustomMaskEdit.GetEditText: string;
  651. begin
  652.   Result := inherited Text;
  653. end;
  654.  
  655. function TCustomMaskEdit.GetTextLen: Integer;
  656. begin
  657.   Result := Length(Text);
  658. end;
  659.  
  660. function TCustomMaskEdit.GetText: string;
  661. begin
  662.   if not IsMasked then
  663.     Result := inherited Text
  664.   else
  665.   begin
  666.     Result := RemoveEditFormat(EditText);
  667.     if FMaskSave then
  668.       Result := AddEditFormat(Result, False);
  669.   end;
  670. end;
  671.  
  672. procedure TCustomMaskEdit.SetText(const Value: string);
  673. var
  674.   OldText: string;
  675.   Pos: Integer;
  676. begin
  677.   if not IsMasked then
  678.     inherited Text := Value
  679.   else
  680.   begin
  681.     OldText := Value;
  682.     if FMaskSave then
  683.       OldText := PadInputLiterals(EditMask, OldText, FMaskBlank)
  684.     else
  685.       OldText := AddEditFormat(OldText, True);
  686.     if not (msDBSetText in FMaskState) and
  687.       (csDesigning in ComponentState) and
  688.       not (csLoading in ComponentState) and
  689.       not Validate(OldText, Pos) then
  690.       raise EDBEditError.Create(LoadStr (SMaskErr));
  691.     EditText := OldText;
  692.   end;
  693. end;
  694.  
  695. procedure TCustomMaskEdit.WMCut(var Message: TMessage);
  696. begin
  697.   if not (IsMasked) then
  698.     inherited
  699.   else
  700.   begin
  701.     CopyToClipboard;
  702.     DeleteKeys(VK_DELETE);
  703.   end;
  704. end;
  705.  
  706. procedure TCustomMaskEdit.WMPaste(var Message: TMessage);
  707. var
  708.   Value: string;
  709.   Str: string;
  710.   SelStart, SelStop : Integer;
  711. begin
  712.   if not (IsMasked) or ReadOnly then
  713.     inherited
  714.   else
  715.   begin
  716.     Clipboard.Open;
  717.     Value := Clipboard.AsText;
  718.     Clipboard.Close;
  719.  
  720.     GetSel(SelStart, SelStop);
  721.     Str := EditText;
  722.     DeleteSelection(Str, SelStart, SelStop - SelStart);
  723.     SelStart := InputString(Str, Value, SelStart);
  724.     EditText := Str;
  725.     SetCursor(SelStart);
  726.   end;
  727. end;
  728.  
  729. function TCustomMaskEdit.GetMasked: Boolean;
  730. begin
  731.   Result := EditMask <> '';
  732. end;
  733.  
  734. function TCustomMaskEdit.GetMaxChars: Integer;
  735. begin
  736.   if IsMasked then
  737.     Result := FMaxChars
  738.   else
  739.     Result := inherited GetTextLen;
  740. end;
  741.  
  742. procedure TCustomMaskEdit.ReformatText(const NewMask: string);
  743. var
  744.   OldText: string;
  745. begin
  746.   OldText := RemoveEditFormat(EditText);
  747.   FEditMask := NewMask;
  748.   FMaxChars  := MaskOffsetToOffset(EditMask, Length(NewMask));
  749.   FMaskSave  := MaskGetMaskSave(NewMask);
  750.   FMaskBlank := MaskGetMaskBlank(NewMask);
  751.   OldText := AddEditFormat(OldText, True);
  752.   EditText := OldText;
  753. end;
  754.  
  755. procedure TCustomMaskEdit.SetEditMask(const Value: string);
  756. var
  757.   SelStart, SelStop: Integer;
  758. begin
  759.   if Value <> EditMask then
  760.   begin
  761.     if (csDesigning in ComponentState) and (Value <> '') and
  762.       not (csLoading in ComponentState) then
  763.       EditText := '';
  764.     if HandleAllocated then GetSel(SelStart, SelStop);
  765.     ReformatText(Value);
  766.     Exclude(FMaskState, msMasked);
  767.     if EditMask <> '' then Include(FMaskState, msMasked);
  768.     inherited MaxLength := 0;
  769.     if IsMasked and (FMaxChars > 0) then
  770.       inherited MaxLength := FMaxChars;
  771.     if HandleAllocated and (GetFocus = Handle) and
  772.        not (csDesigning in ComponentState) then
  773.       SetCursor(SelStart);
  774.   end;
  775. end;
  776.  
  777. function TCustomMaskEdit.GetMaxLength: Integer;
  778. begin
  779.   Result := inherited MaxLength;
  780. end;
  781.  
  782. procedure TCustomMaskEdit.SetMaxLength(Value: Integer);
  783. begin
  784.   if not IsMasked then
  785.     inherited MaxLength := Value
  786.   else
  787.     inherited MaxLength := FMaxChars;
  788. end;
  789.  
  790. procedure TCustomMaskEdit.GetSel(var SelStart: Integer; var SelStop: Integer);
  791. begin
  792.   SendMessage(Handle, EM_GETSEL, Integer(@SelStart), Integer(@SelStop));
  793. end;
  794.  
  795. procedure TCustomMaskEdit.SetSel(SelStart: Integer; SelStop: Integer);
  796. begin
  797.   SendMessage(Handle, EM_SETSEL, SelStart, SelStop);
  798. end;
  799.  
  800. procedure TCustomMaskEdit.SetCursor(Pos: Integer);
  801. var
  802.   SelStart, SelStop: Integer;
  803.   KeyState: TKeyboardState;
  804.   NewKeyState: TKeyboardState;
  805.   I: Integer;
  806. begin
  807.   SelStart := Pos;
  808.   if (IsMasked) then
  809.   begin
  810.     if SelStart < 0 then
  811.       SelStart := 0;
  812.     SelStop  := SelStart + 1;
  813.     if SelStart >= FMaxChars then
  814.     begin
  815.       SelStart := FMaxChars;
  816.       SelStop  := SelStart;
  817.     end;
  818.  
  819.     SetSel(SelStop, SelStop);
  820.  
  821.     if SelStart <> SelStop then
  822.     begin
  823.       GetKeyboardState(KeyState);
  824.       for I := Low(NewKeyState) to High(NewKeyState) do
  825.         NewKeyState[I] := 0;
  826.       NewKeyState [VK_SHIFT] := $81;
  827.       NewKeyState [VK_LEFT] := $81;
  828.       SetKeyboardState(NewKeyState);
  829.       SendMessage(Handle, WM_KEYDOWN, VK_LEFT, 1);
  830.       SendMessage(Handle, WM_KEYUP, VK_LEFT, 1);
  831.       SetKeyboardState(KeyState);
  832.     end;
  833.     FCaretPos := SelStart;
  834.   end
  835.   else
  836.   begin
  837.     if SelStart < 0 then
  838.       SelStart := 0;
  839.     if SelStart >= Length(EditText) then
  840.       SelStart := Length(EditText);
  841.     SetSel(SelStart, SelStart);
  842.   end;
  843. end;
  844.  
  845. procedure TCustomMaskEdit.CheckCursor;
  846. var
  847.   SelStart, SelStop: Integer;
  848. begin
  849.   if not HandleAllocated then  Exit;
  850.   if (IsMasked) then
  851.   begin
  852.     GetSel(SelStart, SelStop);
  853.     if SelStart = SelStop then
  854.       SetCursor(SelStart);
  855.   end;
  856. end;
  857.  
  858. procedure TCustomMaskEdit.Clear;
  859. begin
  860.   Text := '';
  861. end;
  862.  
  863. function TCustomMaskEdit.EditCanModify: Boolean;
  864. begin
  865.   Result := True;
  866. end;
  867.  
  868. procedure TCustomMaskEdit.Reset;
  869. begin
  870.   if Modified then
  871.   begin
  872.     EditText := FOldValue;
  873.     Modified := False;
  874.   end;
  875. end;
  876.  
  877. function TCustomMaskEdit.CharKeys(var CharCode: Char): Boolean;
  878. var
  879.   SelStart, SelStop : Integer;
  880.   Txt: string;
  881. begin
  882.   Result := False;
  883.   if Word(CharCode) = VK_ESCAPE then
  884.   begin
  885.     Reset;
  886.     Exit;
  887.   end;
  888.   if not EditCanModify or ReadOnly then Exit;
  889.   if (Word(CharCode) = VK_BACK) then Exit;
  890.   if (Word(CharCode) = VK_RETURN) then
  891.   begin
  892.     ValidateEdit;
  893.     Exit;
  894.   end;
  895.  
  896.   GetSel(SelStart, SelStop);
  897.   if (SelStop - SelStart) > 1 then
  898.   begin
  899.     DeleteKeys(VK_DELETE);
  900.     SelStart := GetNextEditChar(SelStart);
  901.     SetCursor(SelStart);
  902.   end;
  903.  
  904.   Result := InputChar(CharCode, SelStart);
  905.   if Result then
  906.   begin
  907.     Txt := CharCode;
  908.     SendMessage(Handle, EM_REPLACESEL, 0, LongInt(PChar(Txt)));
  909.     GetSel(SelStart, SelStop);
  910.     CursorInc(SelStart, 0);
  911.   end;
  912. end;
  913.  
  914. procedure TCustomMaskEdit.ArrowKeys(CharCode: Word; Shift: TShiftState);
  915. var
  916.   SelStart, SelStop : Integer;
  917. begin
  918.   if (ssCtrl in Shift) then Exit;
  919.   GetSel(SelStart, SelStop);
  920.   if (ssShift in Shift) then
  921.   begin
  922.     if (CharCode = VK_RIGHT) then
  923.     begin
  924.       Inc(FCaretPos);
  925.       if (SelStop = SelStart + 1) then
  926.       begin
  927.         SetSel(SelStart, SelStop);  {reset caret to end of string}
  928.         Inc(FCaretPos);
  929.       end;
  930.       if FCaretPos > FMaxChars then FCaretPos := FMaxChars;
  931.     end
  932.     else  {if (CharCode = VK_LEFT) then}
  933.     begin
  934.       Dec(FCaretPos);
  935.       if (SelStop = SelStart + 2) and
  936.         (FCaretPos > SelStart) then
  937.       begin
  938.         SetSel(SelStart + 1, SelStart + 1);  {reset caret to show up at start}
  939.         Dec(FCaretPos);
  940.       end;
  941.       if FCaretPos < 0 then FCaretPos := 0;
  942.     end;
  943.   end
  944.   else
  945.   begin
  946.     if (SelStop - SelStart) > 1 then
  947.     begin
  948.       if SelStop = FCaretPos then
  949.         Dec(FCaretPos);
  950.       SetCursor(FCaretPos);
  951.     end
  952.     else if (CharCode = VK_LEFT) then
  953.       CursorDec(SelStart)
  954.     else   { if (CharCode = VK_RIGHT) then  }
  955.     begin
  956.       if SelStop = SelStart then
  957.         SetCursor(SelStart)
  958.       else
  959.         CursorInc(SelStart, 1);
  960.     end;
  961.   end;
  962. end;
  963.  
  964. procedure TCustomMaskEdit.CursorInc(CursorPos: Integer; Incr: Integer);
  965. var
  966.   NuPos: Integer;
  967. begin
  968.   NuPos := CursorPos + Incr;
  969.   NuPos := GetNextEditChar(NuPos);
  970.   if IsLiteralChar(EditMask, nuPos) then
  971.     NuPos := CursorPos;
  972.   SetCursor(NuPos);
  973. end;
  974.  
  975.  
  976. procedure TCustomMaskEdit.CursorDec(CursorPos: Integer);
  977. var
  978.   nuPos: Integer;
  979. begin
  980.   nuPos := CursorPos;
  981.   Dec(nuPos);
  982.   nuPos := GetPriorEditChar(nuPos);
  983.   SetCursor(NuPos);
  984. end;
  985.  
  986. function TCustomMaskEdit.GetFirstEditChar: Integer;
  987. begin
  988.   Result := 0;
  989.   if IsMasked then
  990.     Result := GetNextEditChar(0);
  991. end;
  992.  
  993. function TCustomMaskEdit.GetLastEditChar: Integer;
  994. begin
  995.   Result := GetMaxChars;
  996.   if IsMasked then
  997.     Result := GetPriorEditChar(Result - 1);
  998. end;
  999.  
  1000. function TCustomMaskEdit.GetNextEditChar(Offset: Integer): Integer;
  1001. begin
  1002.   Result := Offset;
  1003.   while(Result < FMaxChars) and (IsLiteralChar(EditMask, Result)) do
  1004.     Inc(Result);
  1005. end;
  1006.  
  1007. function TCustomMaskEdit.GetPriorEditChar(Offset: Integer): Integer;
  1008. begin
  1009.   Result := Offset;
  1010.   while(Result >= 0) and (IsLiteralChar(EditMask, Result)) do
  1011.     Dec(Result);
  1012.   if Result < 0 then
  1013.     Result := GetNextEditChar(Result);
  1014. end;
  1015.  
  1016. procedure TCustomMaskEdit.HomeEndKeys(CharCode: Word; Shift: TShiftState);
  1017. var
  1018.   SelStart, SelStop : Integer;
  1019. begin
  1020.   GetSel(SelStart, SelStop);
  1021.   if (CharCode = VK_HOME) then
  1022.   begin
  1023.     if (ssShift in Shift) then
  1024.     begin
  1025.       if (SelStart <> FCaretPos) and (SelStop <> (SelStart + 1)) then
  1026.         SelStop := SelStart + 1;
  1027.       SetSel(0, SelStop);
  1028.       CheckCursor;
  1029.     end
  1030.     else
  1031.       SetCursor(0);
  1032.     FCaretPos := 0;
  1033.   end
  1034.   else
  1035.   begin
  1036.     if (ssShift in Shift) then
  1037.     begin
  1038.       if (SelStop <> FCaretPos) and (SelStop <> (SelStart + 1)) then
  1039.         SelStart := SelStop - 1;
  1040.       SetSel(SelStart, FMaxChars);
  1041.       CheckCursor;
  1042.     end
  1043.     else
  1044.       SetCursor(FMaxChars);
  1045.     FCaretPos := FMaxChars;
  1046.   end;
  1047. end;
  1048.  
  1049. procedure TCustomMaskEdit.DeleteKeys(CharCode: Word);
  1050. var
  1051.   SelStart, SelStop : Integer;
  1052.   NuSelStart: Integer;
  1053.   Str: string;
  1054. begin
  1055.   if ReadOnly then Exit;
  1056.   GetSel(SelStart, SelStop);
  1057.   if ((SelStop - SelStart) <= 1) and (CharCode = VK_BACK) then
  1058.   begin
  1059.     NuSelStart := SelStart;
  1060.     CursorDec(SelStart);
  1061.     GetSel(SelStart, SelStop);
  1062.     if SelStart = NuSelStart then Exit;
  1063.   end;
  1064.  
  1065.   if (SelStop - SelStart) < 1 then Exit;
  1066.  
  1067.   Str := EditText;
  1068.   DeleteSelection(Str, SelStart, SelStop - SelStart);
  1069.   Str := Copy(Str, SelStart+1, SelStop - SelStart);
  1070.   SendMessage(Handle, EM_REPLACESEL, 0, LongInt(PChar(Str)));
  1071.   if (SelStop - SelStart) <> 1 then
  1072.   begin
  1073.     SelStart := GetNextEditChar(SelStart);
  1074.     SetCursor(SelStart);
  1075.   end
  1076.   else begin
  1077.     GetSel(SelStart, SelStop);
  1078.     SetCursor(SelStart - 1);
  1079.   end;
  1080. end;
  1081.  
  1082. procedure TCustomMaskEdit.CMEnter(var Message: TCMEnter);
  1083. begin
  1084.   if IsMasked and not (csDesigning in ComponentState) then
  1085.   begin
  1086.     if not (msReEnter in FMaskState) then
  1087.     begin
  1088.       FOldValue := EditText;
  1089.       inherited;
  1090.     end;
  1091.     Exclude(FMaskState, msReEnter);
  1092.     CheckCursor;
  1093.   end
  1094.   else
  1095.     inherited;
  1096. end;
  1097.  
  1098. procedure TCustomMaskEdit.CMTextChanged(var Message: TMessage);
  1099. var
  1100.   SelStart, SelStop : Integer;
  1101.   Temp: Integer;
  1102. begin
  1103.   inherited;
  1104.   FOldValue := EditText;
  1105.   if HandleAllocated then
  1106.   begin
  1107.     GetSel(SelStart, SelStop);
  1108.     Temp := GetNextEditChar(SelStart);
  1109.     if Temp <> SelStart then
  1110.       SetCursor(Temp);
  1111.   end;
  1112. end;
  1113.  
  1114. procedure TCustomMaskEdit.CMWantSpecialKey(var Message: TCMWantSpecialKey);
  1115. begin
  1116.   inherited;
  1117.   if (Message.CharCode = VK_ESCAPE) and IsMasked and Modified then
  1118.     Message.Result := 1;
  1119. end;
  1120.  
  1121. procedure TCustomMaskEdit.CMExit(var Message: TCMExit);
  1122. begin
  1123.   if IsMasked and not (csDesigning in ComponentState) then
  1124.   begin
  1125.     ValidateEdit;
  1126.     CheckCursor;
  1127.   end;
  1128.   inherited;
  1129. end;
  1130.  
  1131. procedure TCustomMaskEdit.ValidateEdit;
  1132. var
  1133.   Str: string;
  1134.   Pos: Integer;
  1135. begin
  1136.   Str := EditText;
  1137.   if IsMasked and Modified then
  1138.   begin
  1139.     if not Validate(Str, Pos) then
  1140.     begin
  1141.       if not (csDesigning in ComponentState) then
  1142.       begin
  1143.         Include(FMaskState, msReEnter);
  1144.         SetFocus;
  1145.       end;
  1146.       SetCursor(Pos);
  1147.       ValidateError;
  1148.     end;
  1149.   end;
  1150. end;
  1151.  
  1152. procedure TCustomMaskEdit.ValidateError;
  1153. var
  1154.   Str: string;
  1155. begin
  1156.   MessageBeep(0);
  1157.   Str := EditMask;
  1158.   Str := FmtLoadStr(SMaskEditErr, [Str]);
  1159.   raise EDBEditError.Create(Str);
  1160. end;
  1161.  
  1162. function TCustomMaskEdit.AddEditFormat(const Value: string; Active: Boolean): string;
  1163. begin
  1164.   if not Active then
  1165.     Result := MaskDoFormatText(EditMask, Value, ' ')
  1166.   else
  1167.     Result := MaskDoFormatText(EditMask, Value, FMaskBlank);
  1168. end;
  1169.  
  1170. function TCustomMaskEdit.RemoveEditFormat(const Value: string): string;
  1171. var
  1172.   I: Integer;
  1173.   OldLen: Integer;
  1174.   Offset, MaskOffset: Integer;
  1175.   CType: TMaskCharType;
  1176.   Dir: TMaskDirectives;
  1177. begin
  1178.   Offset := 1;
  1179.   Result := Value;
  1180.   for MaskOffset := 1 to Length(EditMask) do
  1181.   begin
  1182.     CType := MaskGetCharType(EditMask, MaskOffset);
  1183.  
  1184.     if CType in [mcLiteral, mcIntlLiteral] then
  1185.       Result := Copy(Result, 1, Offset - 1) +
  1186.         Copy(Result, Offset + 1, Length(Result) - Offset);
  1187.     if CType in [mcMask, mcMaskOpt] then Inc(Offset);
  1188.   end;
  1189.  
  1190.   Dir := MaskGetCurrentDirectives(EditMask, 1);
  1191.   if mdReverseDir in Dir then
  1192.   begin
  1193.     Offset := 1;
  1194.     for I := 1 to Length(Result) do
  1195.     begin
  1196.       if Result[I] = FMaskBlank then
  1197.         Inc(Offset)
  1198.       else
  1199.         break;
  1200.     end;
  1201.     if Offset <> 1 then
  1202.       Result := Copy(Result, Offset, Length(Result) - Offset + 1);
  1203.   end
  1204.   else begin
  1205.     OldLen := Length(Result);
  1206.     for I := 1 to OldLen do
  1207.     begin
  1208.       if Result[OldLen - I + 1] = FMaskBlank then
  1209.         SetLength(Result, Length(Result) - 1)
  1210.       else Break;
  1211.     end;
  1212.   end;
  1213.   if FMaskBlank <> ' ' then
  1214.   begin
  1215.     OldLen := Length(Result);
  1216.     for I := 1 to OldLen do
  1217.     begin
  1218.       if Result[I] = FMaskBlank then
  1219.         Result[I] := ' ';
  1220.       if I > OldLen then Break;
  1221.     end;
  1222.   end;
  1223. end;
  1224.  
  1225. function TCustomMaskEdit.InputChar(var NewChar: Char; Offset: Integer): Boolean;
  1226. var
  1227.   MaskOffset: Integer;
  1228.   CType: TMaskCharType;
  1229.   InChar: Char;
  1230. begin
  1231.   Result := True;
  1232.   if EditMask <> '' then
  1233.   begin
  1234.     Result := False;
  1235.     MaskOffset := OffsetToMaskOffset(EditMask, Offset);
  1236.     if MaskOffset >= 0 then
  1237.     begin
  1238.       CType := MaskGetCharType(EditMask, MaskOffset);
  1239.       InChar := NewChar;
  1240.       Result := DoInputChar(NewChar, MaskOffset);
  1241.       if not Result and (CType in [mcMask, mcMaskOpt]) then
  1242.       begin
  1243.         MaskOffset := FindLiteralChar (MaskOffset, InChar);
  1244.         if MaskOffset > 0 then
  1245.         begin
  1246.           MaskOffset := MaskOffsetToOffset(EditMask, MaskOffset);
  1247.           SetCursor (MaskOffset);
  1248.           Exit;
  1249.         end;
  1250.       end;
  1251.     end;
  1252.   end;
  1253.   if not Result then
  1254.     MessageBeep(0)
  1255. end;
  1256.  
  1257. function TCustomMaskEdit.DoInputChar(var NewChar: Char; MaskOffset: Integer): Boolean;
  1258. var
  1259.   Dir: TMaskDirectives;
  1260.   Str: string;
  1261.   CType: TMaskCharType;
  1262. begin
  1263.   Result := True;
  1264.   CType := MaskGetCharType(EditMask, MaskOffset);
  1265.   if CType in [mcLiteral, mcIntlLiteral] then
  1266.     NewChar := MaskIntlLiteralToChar(EditMask[MaskOffset])
  1267.   else
  1268.   begin
  1269.     Dir := MaskGetCurrentDirectives(EditMask, MaskOffset);
  1270.     case EditMask[MaskOffset] of
  1271.       mMskNumeric, mMskNumericOpt:
  1272.         begin
  1273.           if not ((NewChar >= '0') and (NewChar <= '9')) then
  1274.             Result := False;
  1275.         end;
  1276.       mMskNumSymOpt:
  1277.         begin
  1278.           if not (((NewChar >= '0') and (NewChar <= '9')) or
  1279.                  (NewChar = ' ') or(NewChar = '+') or(NewChar = '-')) then
  1280.             Result := False;
  1281.         end;
  1282.       mMskAscii, mMskAsciiOpt:
  1283.         begin
  1284.           if IsCharAlpha(NewChar) then
  1285.           begin
  1286.             Str := ' ';
  1287.             Str[1] := NewChar;
  1288.             if (mdUpperCase in Dir)  then
  1289.               Str := AnsiUpperCase(Str)
  1290.             else if mdLowerCase in Dir then
  1291.               Str := AnsiLowerCase(Str);
  1292.             NewChar := Str[1];
  1293.           end;
  1294.         end;
  1295.       mMskAlpha, mMskAlphaOpt, mMskAlphaNum, mMskAlphaNumOpt:
  1296.         begin
  1297.           Str := ' ';
  1298.           Str[1] := NewChar;
  1299.           if not IsCharAlpha(NewChar) then
  1300.           begin
  1301.             Result := False;
  1302.             if ((EditMask[MaskOffset] = mMskAlphaNum) or
  1303.                (EditMask[MaskOffset] = mMskAlphaNumOpt)) and
  1304.                 IsCharAlphaNumeric(NewChar) then
  1305.               Result := True;
  1306.           end
  1307.           else if mdUpperCase in Dir then
  1308.             Str := AnsiUpperCase(Str)
  1309.           else if mdLowerCase in Dir then
  1310.             Str := AnsiLowerCase(Str);
  1311.           NewChar := Str[1];
  1312.         end;
  1313.     end;
  1314.   end;
  1315. end;
  1316.  
  1317. function TCustomMaskEdit.Validate(const Value: string; var Pos: Integer): Boolean;
  1318. var
  1319.   Offset, MaskOffset: Integer;
  1320.   CType: TMaskCharType;
  1321. begin
  1322.   Result := True;
  1323.   Offset := 1;
  1324.   for MaskOffset := 1 to Length(EditMask) do
  1325.   begin
  1326.     CType := MaskGetCharType(EditMask, MaskOffset);
  1327.  
  1328.     if CType in [mcLiteral, mcIntlLiteral, mcMaskOpt] then
  1329.       Inc(Offset)
  1330.     else if (CType = mcMask) and (Value <> '') then
  1331.     begin
  1332.       if (Value [Offset] = FMaskBlank) or
  1333.         ((Value [Offset] = ' ') and (EditMask[MaskOffset] <> mMskAscii)) then
  1334.       begin
  1335.         Result := False;
  1336.         Pos := Offset - 1;
  1337.         Exit;
  1338.       end;
  1339.       Inc(Offset);
  1340.     end;
  1341.   end;
  1342. end;
  1343.  
  1344. function TCustomMaskEdit.DeleteSelection(var Value: string; Offset: Integer;
  1345.   Len: Integer): Boolean;
  1346. var
  1347.   EndDel: Integer;
  1348.   StrOffset, MaskOffset, Temp: Integer;
  1349.   CType: TMaskCharType;
  1350. begin
  1351.   Result := True;
  1352.  
  1353.   StrOffset := Offset + 1;
  1354.   EndDel := StrOffset + Len;
  1355.   Temp := OffsetToMaskOffset(EditMask, Offset);
  1356.   if Temp < 0 then  Exit;
  1357.   for MaskOffset := Temp to Length(EditMask) do
  1358.   begin
  1359.     CType := MaskGetCharType(EditMask, MaskOffset);
  1360.     if CType in [mcLiteral, mcIntlLiteral] then
  1361.       Inc(StrOffset)
  1362.     else if CType in [mcMask, mcMaskOpt] then
  1363.     begin
  1364.       Value[StrOffset] := FMaskBlank;
  1365.       Inc(StrOffset);
  1366.     end;
  1367.     if StrOffset > EndDel then
  1368.       Break;
  1369.   end;
  1370. end;
  1371.  
  1372. function TCustomMaskEdit.InputString(var Value: string; const NewValue: string;
  1373.   Offset: Integer): Integer;
  1374. var
  1375.   NewOffset, MaskOffset, Temp: Integer;
  1376.   CType: TMaskCharType;
  1377.   NewVal: string;
  1378.   NewChar: Char;
  1379. begin
  1380.   Result := Offset;
  1381.   if NewValue = '' then Exit;
  1382.   { replace chars with new chars, except literals }
  1383.   NewOffset := 1;
  1384.   NewVal := NewValue;
  1385.   Temp := OffsetToMaskOffset(EditMask, Offset);
  1386.   if Temp < 0 then  Exit;
  1387.   for MaskOffset := Temp to Length(EditMask) do
  1388.   begin
  1389.     CType := MaskGetCharType(EditMask, MaskOffset);
  1390.     if CType in [mcLiteral, mcIntlLiteral, mcMask, mcMaskOpt] then
  1391.     begin
  1392.       NewChar := NewVal[NewOffset];
  1393.       if not (DoInputChar(NewChar, MaskOffset)) then
  1394.         NewChar := FMaskBlank;
  1395.         { if pasted text does not contain a literal in the right place,
  1396.           insert one }
  1397.       if not ((CType in [mcLiteral, mcIntlLiteral]) and
  1398.         (NewChar <> NewVal[NewOffset])) then
  1399.         NewVal[NewOffset] := NewChar
  1400.       else
  1401.         NewVal := Copy(NewVal, 1, NewOffset-1) + NewChar +
  1402.           Copy(NewVal, NewOffset, Length (NewVal));
  1403.       Inc(NewOffset);
  1404.     end;
  1405.     if (NewOffset + Offset) > FMaxChars then Break;
  1406.     if (NewOffset) > Length(NewVal) then Break;
  1407.   end;
  1408.  
  1409.   if (Offset + Length(NewVal)) < FMaxChars then
  1410.   begin
  1411.     Value := Copy(Value, 1, Offset) + NewVal +
  1412.       Copy(Value, OffSet + Length(NewVal) + 1,
  1413.         FMaxChars -(Offset + Length(NewVal)));
  1414.   end
  1415.   else
  1416.   begin
  1417.     Value := Copy(Value, 1, Offset) +
  1418.              Copy(NewVal, 1, FMaxChars - Offset);
  1419.   end;
  1420.   Result := NewOffset + Offset - 1;
  1421. end;
  1422.  
  1423. function TCustomMaskEdit.FindLiteralChar(MaskOffset: Integer; InChar: Char): Integer;
  1424. var
  1425.   CType: TMaskCharType;
  1426.   LitChar: Char;
  1427. begin
  1428.   Result := -1;
  1429.   while MaskOffset < Length(EditMask) do
  1430.   begin
  1431.     Inc(MaskOffset);
  1432.     CType := MaskGetCharType(EditMask, MaskOffset);
  1433.     if CType in [mcLiteral, mcIntlLiteral] then
  1434.     begin
  1435.       LitChar := EditMask[MaskOffset];
  1436.       if CType = mcIntlLiteral then
  1437.         LitChar := MaskIntlLiteralToChar(LitChar);
  1438.       if LitChar = InChar then
  1439.         Result := MaskOffset;
  1440.       Exit;
  1441.     end;
  1442.   end;
  1443. end;
  1444.  
  1445. end.
  1446.