home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 2000 March / pcp161b.iso / full / delphi / RUNIMAGE / DELPHI30 / SOURCE / VCL / MASK.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-08-03  |  41.0 KB  |  1,533 lines

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