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