home *** CD-ROM | disk | FTP | other *** search
/ Delphi Anthology / aDELPHI.iso / Runimage / Delphi50 / Source / Vcl / mask.pas < prev    next >
Pascal/Delphi Source File  |  1999-08-11  |  43KB  |  1,544 lines

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