Unit Mask;

Interface

Uses Dos,Messages,SysUtils,Classes,Buttons,Forms,StdCtrls,Dialogs;

Const
   DefaultBlank:Char='_';
   MaskFieldSeparator:Char=';';
   MaskNoSave:Char='0';

   mMskTimeSeparator=':';
   mMskDateSeparator='/';
   mMskAlpha='L';
   mMskAlphaOpt='l';
   mMskAlphaNum='A';
   mMskAlphaNumOpt='a';
   mMskAscii='C';
   mMskAsciiOpt='c';
   mMskNumeric='0';
   mMskNumericOpt='9';
   mMskNumSymOpt='#';

   mDirReverse='!';
   mDirUpperCase='>';
   mDirLowerCase='<';
   mDirLiteral='\';

   EditTextExceptionMsg='Value in the edit field is invalid.'#13#10'Use escape key to cancel changes !';

Type
   EEditTextInvalid=Class(Exception);

   TOnEditTextInvalid=Procedure(Sender:TObject) Of Object;

   TMaskEdit=Class(TEdit)
      Private
             FEditMask:String;
             FCanvas:TCanvas;
             FMaskBlank:Char;
             FMaskSave:Boolean;
             FOnEditTextInvalid:TOnEditTextInvalid;
      Private
             Procedure SetEditMask(Const NewValue:String);
             Function GetCursorPos:LongInt;
             Function GetCurrentMask(Position:LongInt;Var UpLowCase:Byte;Var Blanks:Boolean):PChar;
             Procedure UpdateCursorPos;
             Function GetText:String;
             Procedure SetText(Const NewValue:String);
             Function GetIsMasked:Boolean;
             Function GetEditText:String;
             Procedure SetEditText(Const NewValue:String);
             Procedure CloseQuery(Sender:TObject;Var CanClose:Boolean);
      Protected
             Procedure EditTextInvalid;Virtual;
             Procedure CharEvent(Var key:Char;RepeatCount:Byte);Override;
             Procedure ScanEvent(Var KeyCode:TKeyCode;RepeatCount:Byte);Override;
             Procedure MouseMove(ShiftState:TShiftState;X,Y:LongInt);Override;
             Procedure SetupComponent;Override;
             Procedure SetupShow;Override;
             Procedure ParentNotification(Var Msg:TMessage);Override;
      Public
             Function ValidateEdit:Boolean;
      Protected
             Property InsertMode;
             Property OnCloseQuery;
      Public
             Property IsMasked:Boolean read GetIsMasked;
             Property EditText:String read GetEditText write SetEditText;
             Property MaskBlank:Char read FMaskBlank;
             Property MaskSave:Boolean read FMaskSave;
      Published
             Property EditMask:String read FEditMask write SetEditMask;
             Property Text:String read GetText write SetText;
             Property OnEditTextInvalid:TOnEditTextInvalid read FOnEditTextInvalid write FOnEditTextInvalid;
   End;

Function InsertMaskEdit(parent:TControl;Left,Bottom,Width,Height:LongInt;
                        Text,Hint:String):TMaskEdit;

Implementation

Function InsertMaskEdit(parent:TControl;Left,Bottom,Width,Height:LongInt;
                    Text,Hint:String):TMaskEdit;
Begin
     Result.Create(parent);
     Result.SetWindowPos(Left,Bottom,Width,Height);
     Result.Text:=Text;
     Result.Hint:=Hint;
     Result.AutoSize:=True;
     Result.parent := parent;
End;


{$IFDEF OS2}
Uses PmWin;
{$ENDIF}

{
浜様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様�
�                                                                           �
� Speed-Pascal/2 Version 2.0                                                �
�                                                                           �
� Speed-Pascal Component Classes (SPCC)                                     �
�                                                                           �
� This section: TMaskEdit Class implementation                              �
�                                                                           �
� Last modified: January 1998                                               �
�                                                                           �
� (C) 1998 SpeedSoft. All rights reserved. Disclosure probibited !          �
�                                                                           �
藩様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様�
}

Procedure TMaskEdit.SetupComponent;
Begin
     Inherited SetupComponent;

     Name:='MaskEdit';
     FMaskBlank:=DefaultBlank;
     FMaskSave:=False;
     InsertMode:=False;
     OnCloseQuery:=CloseQuery;
End;

Procedure TMaskEdit.EditTextInvalid;
Begin
     If FOnEditTextInvalid<>Nil Then FOnEditTextInvalid(Self)
     Else Raise EEditTextInvalid.Create(EditTextExceptionMsg);
End;

Procedure TMaskEdit.ParentNotification(Var Msg:TMessage);
Begin
     Inherited ParentNotification(Msg);

     {$IFDEF OS2}
     If Msg.Param1Hi=EN_KILLFOCUS Then
     Begin
          Try
             If not ValidateEdit Then EditTextInvalid;
             InsertMode:=True;
          Except
             On E:EEditTextInvalid Do
             Begin
                  ErrorBox(E.Message);
                  Focus;
             End;
          End;
     End
     Else If Msg.Param1Hi=EN_SETFOCUS Then InsertMode:=False;
     {$ENDIF}
End;

Procedure TMaskEdit.CloseQuery(Sender:TObject;Var CanClose:Boolean);
Begin
     CanClose:=True;
     Try
        If not ValidateEdit Then EditTextInvalid;
     Except
        On E:EEditTextInvalid Do
        Begin
             ErrorBox(E.Message);
             Focus;
             CanClose:=False;
        End;
     End;
End;

Function TMaskEdit.ValidateEdit:Boolean;
Var s,s1:String;
    t:LongInt;
    t1:LongInt;
    CurrentMask:^CString;
    UpLowCase:Byte;
    Blanks:Boolean;
Label error;
Begin
     //validate EditText
     Result:=True; //Blanks are ok (required characters versus optional characters)
     s:=EditText;

     t:=1;
     t1:=1;
     While t<=254 Do
     Begin
          If t1>length(FEditMask) Then
          Begin
               SetLength(s,t-1);
               break;
          End;
          CurrentMask:=@FEditMask[t1];
          If t>length(s) Then break;

          If CurrentMask^[0]=MaskFieldSeparator Then
          Begin
               SetLength(s,t-1);
               break;
          End
          Else If CurrentMask<>Nil Then
          Begin
               Case CurrentMask^[0] Of
                 mDirLiteral:
                 Begin
                    if s[t]<>CurrentMask^[1] Then goto error;
                    t:=t+1;
                    t1:=t1+1;
                 End;
                 mDirUpperCase,mDirLowerCase,mDirReverse:;
                 mMskAlpha,mMskAlphaOpt:
                 Begin
                      If s[t] In ['A'..'Z','a'..'z'] Then
                      Begin
                      End
                      Else If s[t]=FMaskBlank Then
                      Begin
                           If CurrentMask^[0]<>mMskAlphaOpt Then Result:=False;
                      End
                      Else goto error;
                      t:=t+1;
                 End;
                 mMskAlphaNum,mMskAlphaNumOpt:
                 Begin
                      If s[t] In ['A'..'Z','a'..'z','0'..'9'] Then
                      Begin
                      End
                      Else If s[t]=FMaskBlank Then
                      Begin
                           If CurrentMask^[0]<>mMskAlphaNumOpt Then Result:=False;
                      End
                      Else goto error;
                      t:=t+1;
                 End;
                 mMskAscii,mMskAsciiOpt:
                 Begin
                      t:=t+1;
                 End;
                 mMskNumeric,mMskNumericOpt:
                 Begin
                      If s[t] In ['0'..'9'] Then
                      Begin
                      End
                      Else If s[t]=FMaskBlank Then
                      Begin
                          If CurrentMask^[0]<>mMskNumericOpt Then Result:=False;
                      End
                      Else goto error;
                      t:=t+1;
                 End;
                 mMskNumSymOpt:
                 Begin
                      If s[t] In ['+','-','0'..'9'] Then
                      Begin
                      End
                      Else If s[t]<>FMaskBlank Then goto error;
                      t:=t+1;
                 End;
                 mMskTimeSeparator:
                 Begin
                      If s[t]<>TimeSeparator Then goto error;
                      t:=t+1;
                 End;
                 mMskDateSeparator:
                 Begin
                      If s[t]<>DateSeparator Then goto error;
                      t:=t+1;
                 End;
                 Else
                 Begin
                    If s[t]<>CurrentMask^[0] Then goto error;
                    t:=t+1;
                 End;
               End; //case
          End
          Else
          Begin
error:
               EditTextInvalid;
               break;
          End;
          t1:=t1+1;
     End;

     If t<=length(s) Then goto error;
     If t1<=length(FEditMask) Then
      If FEditMask[t1]<>MaskFieldSeparator Then
     Begin
          While t1<=length(FEditMask) Do
          Begin
               CurrentMask:=@FEditMask[t1];
               Case CurrentMask^[0] Of
                  mDirLiteral:
                  Begin
                     s:=s+CurrentMask^[1];
                     t1:=t1+1;
                  End;
                  mDirUpperCase,mDirLowerCase,mDirReverse:;
                  mMskTimeSeparator:s:=s+TimeSeparator;
                  mMskDateSeparator:s:=s+DateSeparator;
                  mMskAlpha,mMskAlphaNum,mMskAscii,mMskNumeric:goto error;
                  mMskAlphaOpt,mMskAlphaNumOpt,mMskAsciiOpt,
                  mMskNumericOpt,mMskNumSymOpt:s:=s+FMaskBlank;
                  Else
                  Begin
                     s:=s+CurrentMask^[0];
                  End;
               End; //Case

               t1:=t1+1;
          End;
     End;

     If s<>EditText Then EditText:=s;
End;

Function TMaskEdit.GetText:String;
Var t,t1:Longint;
    CurrentMask:^CString;
Begin
    Result:=EditText;

    //delete optional literals and blanks if they're part of the mask
    //and they should not be included into text
    If Result<>'' Then If not FMaskSave Then
    Begin
         t1:=1;
         t:=1;
         While t1<=Length(FEditMask) Do
         Begin
              CurrentMask:=@FEditMask[t1];
              Case CurrentMask^[0] Of
                 mDirLiteral:
                 Begin
                    System.Delete(Result,t,1);
                    t1:=t1+1;
                 End;
                 mDirUpperCase,mDirLowerCase,mDirReverse:;
                 mMskAlpha,mMskAlphaOpt,
                 mMskAlphaNum,mMskAlphaNumOpt,
                 mMskAscii,mMskAsciiOpt,
                 mMskNumeric,mMskNumericOpt,
                 mMskNumSymOpt:
                 Begin
                     If Result[t]=FMaskBlank Then System.Delete(Result,t,1)
                     Else t:=t+1;
                 End;
                 mMskTimeSeparator,mMskDateSeparator:
                 Begin
                      System.Delete(Result,t,1);
                 End;
                 Else
                 Begin
                      System.Delete(Result,t,1);
                 End;
              End; //case
              t1:=t1+1;
         End;
    End;
End;

Procedure TMaskEdit.SetText(Const NewValue:String);
Var s,s1:String;
    t,t1:LongInt;
    CurrentMask:^CString;
    UpLowCase:Byte;
    Blanks:Boolean;
Begin
     s:=NewValue;
     t:=1;
     t1:=1;
     While t<=254 Do
     Begin
          If t1>length(FEditMask) Then
          Begin
               SetLength(s,t-1);
               break;
          End;
          CurrentMask:=@FEditMask[t1];
          If t>length(s) Then
          Begin
               SetLength(s,t);
               s[t]:=#32;
          End;
          If s[t]=#32 Then s[t]:=FMaskBlank;

          If CurrentMask^[0]=MaskFieldSeparator Then
          Begin
               SetLength(s,t-1);
               break;
          End
          Else If CurrentMask<>Nil Then
          Begin
               Case CurrentMask^[0] Of
                 mDirLiteral:
                 Begin
                    If FMaskSave Then s[t]:=CurrentMask^[1]
                    Else
                    Begin
                        s1:=CurrentMask^[1];
                        System.Insert(s1,s,t);
                    End;
                    t:=t+1;
                    t1:=t1+1;
                 End;
                 mDirUpperCase,mDirLowerCase,mDirReverse:;
                 mMskAlpha,mMskAlphaOpt:
                 Begin
                      If s[t] In ['A'..'Z','a'..'z'] Then
                      Begin
                           If UpLowCase=1 Then s[t]:=Upcase(s[t])
                           Else If UpLowCase=2 Then
                           Begin
                                s1:=s[t];
                                s1:=LowerCase(s1);
                                s[t]:=s1[1];
                           End;
                      End
                      Else s[t]:=FMaskBlank;
                      t:=t+1;
                 End;
                 mMskAlphaNum,mMskAlphaNumOpt:
                 Begin
                      If s[t] In ['A'..'Z','a'..'z','0'..'9'] Then
                      Begin
                           If UpLowCase=1 Then s[t]:=Upcase(s[t])
                           Else If UpLowCase=2 Then
                           Begin
                                s1:=s[t];
                                s1:=LowerCase(s1);
                                s[t]:=s1[1];
                           End;
                      End
                      Else s[t]:=FMaskBlank;
                      t:=t+1;
                 End;
                 mMskAscii,mMskAsciiOpt:
                 Begin
                      If UpLowCase=1 Then s[t]:=Upcase(s[t])
                      Else If UpLowCase=2 Then
                      Begin
                           s1:=s[t];
                           s1:=LowerCase(s1);
                           s[t]:=s1[1];
                      End;
                      t:=t+1;
                 End;
                 mMskNumeric,mMskNumericOpt:
                 Begin
                      If s[t] In ['0'..'9'] Then
                      Begin
                      End
                      Else s[t]:=FMaskBlank;
                      t:=t+1;
                 End;
                 mMskNumSymOpt:
                 Begin
                      If s[t] In ['+','-','0'..'9'] Then
                      Begin
                      End
                      Else s[t]:=FMaskBlank;
                      t:=t+1;
                 End;
                 mMskTimeSeparator:
                 Begin
                      If FMaskSave Then s[t]:=TimeSeparator
                      Else
                      Begin
                          s1:=TimeSeparator;
                          Insert(s1,s,t);
                      End;
                      t:=t+1;
                 End;
                 mMskDateSeparator:
                 Begin
                      If FMaskSave Then s[t]:=DateSeparator
                      Else
                      Begin
                          s1:=DateSeparator;
                          Insert(s1,s,t);
                      End;
                      t:=t+1;
                 End;
                 Else
                 Begin
                    If FMaskSave Then s[t]:=CurrentMask^[0]
                    Else
                    Begin
                        s1:=CurrentMask^[0];
                        System.Insert(s1,s,t);
                    End;
                    t:=t+1;
                 End;
               End; //case
          End
          Else break;
          t1:=t1+1;
     End;

     EditText:=s;
End;

Function TMaskEdit.GetIsMasked:Boolean;
Begin
     Result:=FEditMask<>'';
End;

Function TMaskEdit.GetEditText:String;
Begin
     Result:=Inherited Text;
End;

Procedure TMaskEdit.SetEditText(Const NewValue:String);
Var s:String;
Begin
     s:=Inherited Text;
     Inherited Text:=NewValue;
     //check if the text is valid
     Try
        ValidateEdit;
     Except
        //Ignore error
        Inherited Text:=s;
     End;
End;

Procedure TMaskEdit.SetEditMask(Const NewValue:String);
Var s,s1:String;
    t:LongInt;
    SepCount:Byte;
Begin
     FEditMask:=NewValue;

     s:=FEditMask;
     t:=1;
     SepCount:=0;

     s1:=s;
     For t:=1 To Length(s) Do s1[t]:=#0;

     t:=1;
     While t<=length(s) Do
     Begin
          If s[t]=MaskFieldSeparator Then
          Begin
               If SepCount=0 Then
               Begin
                    t:=t+1;
                    If ((t<=length(s))And(s[t]<>MaskNoSave)) Then
                      FMaskSave:=True
                    Else
                      FMaskSave:=False;
                    t:=t+1;
                    If s[t]<>MaskFieldSeparator Then
                    Begin
                         SetLength(s,t-1);
                         SetLength(s1,t-1);
                    End;
                    inc(SepCount);
               End
               Else
               Begin
                    t:=t+1;
                    If t<=length(s) Then
                    Begin
                         FMaskBlank:=s[t];
                         SetLength(s,t-4);
                         SetLength(s1,t-4);
                    End
                    Else
                    Begin
                         SetLength(s,t-5);
                         SetLength(s1,t-5);
                    End;
               End;
          End
          Else Case s[t] Of
             mDirLiteral:
             Begin
                  Delete(s,t,1);
                  Delete(s1,t,1);
                  t:=t+1;
             End;
             mDirUpperCase,mDirLowerCase,mDirReverse:
             Begin
                  Delete(s,t,1);
                  Delete(s1,t,1);
             End;
             mMskAlpha,mMskAlphaOpt,mMskAlphaNum,mMskAlphaNumOpt,
             mMskAscii,mMskAsciiOpt,mMskNumeric,mMskNumericOpt,mMskNumSymOpt:
             Begin
                  s1[t]:=FMaskBlank;
                  t:=t+1;
             End;
             mMskTimeSeparator,mMskDateSeparator:t:=t+1;
             Else t:=t+1;
          End; //case
     End;

     For t:=1 To length(s) Do If s1[t]<>#0 Then s[t]:=FMaskBlank;

     Text:=s;
End;

Procedure TMaskEdit.SetupShow;
Begin
     Inherited SetupShow;

     FCanvas.Create(Self);
     FCanvas.Init;
End;

Function TMaskEdit.GetCursorPos:LongInt;
Var
    {$IFDEF OS2}
    Info:CursorInfo;
    {$ENDIF}
    s,s1:String;
    X:LongInt;
    t:LongInt;
    H:LongInt;
    Add,Start:LongInt;
Begin
     Result:=-1;
     {$IFDEF OS2}
     If FCanvas=Nil Then exit;
     Info.hwnd:=Handle;
     If WinQueryCursorInfo(HWND_DESKTOP,Info) Then
     If Info.hwnd=Handle Then
     Begin
          Result:=Info.X;
          s:=Text;
          If BorderStyle=bsNone Then Add:=0
          Else Add:=3;
          X:=Add;
          t:=0;
          Start:=WinSendMsg(Handle,EM_QUERYFIRSTCHAR,0,0);
          FCanvas.Font:=Font;

          While ((Start+t+1<=length(s))And(X<Result)) Do
          Begin
               t:=t+1;
               s1:=Copy(s,Start+1,t);
               FCanvas.GetTextExtent(s1,X,H);
               X:=X+Add;
          End;

          If X>=Result Then Result:=Start+t
          Else Result:=0;
     End;
     {$ENDIF}
End;

Procedure TMaskEdit.MouseMove(ShiftState:TShiftState;X,Y:LongInt);
Begin
End;

Function TMaskEdit.GetCurrentMask(Position:LongInt;
                                  Var UpLowCase:Byte;Var Blanks:Boolean):PChar;
Var t:LongInt;
    EditPos:LongInt;
Begin
     Result:=Nil;
     UpLowCase:=0;
     Blanks:=False;

     If Position>=0 Then
     Begin
         EditPos:=0;
         t:=1;
         While t<=length(FEditMask) Do
         Begin
              If FEditMask[t]=MaskFieldSeparator Then exit;

              If Position=EditPos Then
               If not (FEditMask[t] In [mDirUpperCase,mDirLowerCase,mDirReverse]) Then
              Begin
                   Result:=@FEditMask[t];
                   exit;
              End;

              Case FEditMask[t] Of
                 mDirLiteral:  //Literal character
                 Begin
                      t:=t+2;
                      inc(EditPos);
                 End;
                 mDirUpperCase: //all upcase
                 Begin
                      UpLowCase:=1;
                      t:=t+1;
                 End;
                 mDirLowerCase: //all lowcase
                 Begin
                      UpLowCase:=2;
                      t:=t+1;
                 End;
                 mDirReverse: //blanks
                 Begin
                      Blanks:=True;
                      t:=t+1;
                 End;
                 Else
                 Begin
                      t:=t+1;
                      inc(EditPos);
                 End;
              End;
         End;
     End;
End;

Procedure TMaskEdit.UpdateCursorPos;
Var Position,Pos1:LongInt;
    CurrentMask:^CString;
    UpLowCase:Byte;
    Blanks:Boolean;
Label again;
Begin
     Position:=GetCursorPos;
again:
     If ((Position<0)Or(Position>length(FEditMask))) Then exit;
     CurrentMask:=GetCurrentMask(Position,UpLowCase,Blanks);
     If CurrentMask=Nil Then exit;

     Case CurrentMask^[0] Of
        mMskAlpha,mMskAlphaOpt,mMskAlphaNum,mMskAlphaNumOpt,
        mMskAscii,mMskAsciiOpt,mMskNumeric,mMskNumericOpt,mMskNumSymOpt:;
        Else
        Begin
             {$IFDEF OS2}
             WinSendMsg(Handle,WM_CHAR,KC_VIRTUALKEY OR KC_SCANCODE OR KC_TOGGLE,
                         224 Or (VK_RIGHT SHL 16));
             {$ENDIF}
             Pos1:=GetCursorPos;
             If Pos1<>Position Then
             Begin
                  Position:=Pos1;
                  goto again;
             End;
        End;
     End;
End;

Procedure TMaskEdit.CharEvent(Var key:Char;RepeatCount:Byte);
Var Position:LongInt;
    CurrentMask:PChar;
    UpLowCase:Byte;
    Blanks:Boolean;
    Valid:Boolean;
    s:String;
    Msg:PMessage;
Begin
     If not IsMasked Then
     Begin
          Inherited CharEvent(key,RepeatCount);
          exit;
     End;

     UpdateCursorPos;

     Position:=GetCursorPos;

     If ((Position<0)Or(Position>length(FEditMask))) Then
     Begin
         key:=#0;
         exit;
     End;

     CurrentMask:=GetCurrentMask(Position,UpLowCase,Blanks);
     If CurrentMask=Nil Then
     Begin
          key:=#0;
          exit;
     End;

     If key=#32 Then
     Begin
          key:=FMaskBlank;
          Valid:=True;
     End
     Else
     Begin
          Valid:=False;
          Case CurrentMask^[0] Of
             mDirLiteral:;
             mMskAlpha,mMskAlphaOpt: //Alpha
             Begin
                  If Key In ['A'..'Z','a'..'z'] Then
                    Valid:=True;
             End;
             mMskAlphaNum,mMskAlphaNumOpt: //Alpha and Num
             Begin
                  If Key In ['A'..'Z','a'..'z','0'..'9'] Then
                    Valid:=True;
             End;
             mMskNumeric,mMskNumericOpt: //any number
             Begin
                  If Key In ['0'..'9'] Then
                    Valid:=True;
             End;
             mMskNumSymOpt: //any number and +,-
             Begin
                  If Key In ['0'..'9','+','-'] Then
                    Valid:=True;
             End;
             mMskAscii,mMskAsciiOpt: //Any Char
             Begin
                  Valid:=True;
             End;
          End;
     End;

     If Valid Then
     Begin
          If UpLowCase=1 Then Key:=Upcase(Key)
          Else If UpLowCase=2 Then
          Begin
               s:=LowerCase(Key);
               Key:=s[1];
          End;
          Inherited CharEvent(key,RepeatCount);
     End
     Else key:=#0;

     If key<>#0 Then
     Begin
          Asm
             PUSH DWORD PTR SELF
             CALLN32 Forms.GetLastMsgAdr
             MOV Msg,EAX
          End;
          If Msg<>Nil Then TWMCHAR(Msg^).CharCode:=ord(key);
          LastMsg.CallDefaultHandler;
          UpdateCursorPos;
     End;
End;

Procedure TMaskEdit.ScanEvent(Var KeyCode:TKeyCode;RepeatCount:Byte);
Var Position,Pos1:LongInt;
    CurrentMask:PChar;
    UpLowCase:Byte;
    Blanks:Boolean;
    s:String;
    t:LongInt;
    SStart,SLen:LongInt;
Label again;
Begin
     If not IsMasked Then
     Begin
          Inherited ScanEvent(KeyCode,RepeatCount);
          exit;
     End;

     If keycode<>kbNull Then
     Begin
        Case keycode of
          kbIns:
          Begin
               KeyCode:=kbNull;
               exit;
          End;
          kbShiftIns:  //Insert from clipboard
          Begin
               {$IFDEF OS2}
               If ClipBoard.HasFormat(CF_TEXT) Then
               Begin
                   s:=Inherited Text;
                   ClipBoard.Open(Handle);
                   CurrentMask:=PChar(ClipBoard.GetData(CF_TEXT));
                   If CurrentMask<>Nil Then Inherited Text:=CurrentMask^;
                   ClipBoard.Close;
                   //check if the text is valid
                   Try
                      ValidateEdit;
                   Except
                      //Ignore error
                      Inherited Text:=s;
                   End;
               End;
               {$ENDIF}
               KeyCode:=kbNull;
               exit;
          End;
          kbBkSp:
          Begin
               {$IFDEF OS2}
               WinSendMsg(Handle,WM_CHAR,KC_VIRTUALKEY OR KC_SCANCODE OR KC_TOGGLE,
                          224 Or (VK_LEFT SHL 16));
               WinSendMsg(Handle,WM_CHAR,KC_CHAR OR KC_TOGGLE OR KC_SCANCODE OR KC_VIRTUALKEY Or
                          (57 Shl 24),
                          32 Or (VK_SPACE Shl 16));
               WinSendMsg(Handle,WM_CHAR,KC_VIRTUALKEY OR KC_SCANCODE OR KC_TOGGLE,
                          224 Or (VK_LEFT SHL 16));
               {$ENDIF}
               keycode:=kbNull;
               exit;
          End;
          kbDel:
          Begin
               //check selection
               If ((SelStart>=0)And(SelLength>1)) Then
               Begin
                    SStart:=SelStart;
                    SLen:=SelLength;

                    s:=Text;
                    For t:=SelStart+1 To SelStart+SelLength Do
                    Begin
                         CurrentMask:=GetCurrentMask(t-1,UpLowCase,Blanks);
                         If CurrentMask<>Nil Then
                         Begin
                              If CurrentMask^[0] In
                                [mMskAlpha,mMskAlphaOpt,mMskAlphaNum,mMskAlphaNumOpt,
                                 mMskAscii,mMskAsciiOpt,mMskNumeric,mMskNumericOpt,mMskNumSymOpt] Then
                                    s[t]:=FMaskBlank;
                         End;
                    End;
                    Inherited Text:=s;
                    keycode:=kbNull;

                    SelStart:=SStart;
                    SelLength:=SLen;

                    exit;
               End;

               {$IFDEF OS2}
               WinSendMsg(Handle,WM_CHAR,KC_CHAR OR KC_TOGGLE OR KC_SCANCODE OR KC_VIRTUALKEY Or
                          (57 Shl 24),
                          32 Or (VK_SPACE Shl 16));
               WinSendMsg(Handle,WM_CHAR,KC_VIRTUALKEY OR KC_SCANCODE OR KC_TOGGLE,
                          224 Or (VK_LEFT SHL 16));
               {$ENDIF}
               keycode:=kbNull;
               exit;
          End;
          kbCLeft:
          Begin
               Position:=GetCursorPos;
               If Position=1 Then
               Begin
                    CurrentMask:=GetCurrentMask(Position-1,UpLowCase,Blanks);
                    If CurrentMask=Nil Then
                    Begin
                        keycode:=kbNull;
                        exit;
                    End;

                    Case CurrentMask^[0] Of
                       mMskAlpha,mMskAlphaOpt,mMskAlphaNum,mMskAlphaNumOpt,
                       mMskAscii,mMskAsciiOpt,mMskNumeric,mMskNumericOpt,mMskNumSymOpt:;
                       Else If Position>0 Then
                       Begin
                            keycode:=kbNull;
                            exit;
                       End;
                    End;
               End;
          End;
        End; //case

        LastMsg.CallDefaultHandler;

        If KeyCode In [kbCR{$IFDEF OS2},kbEnter{$ENDIF}] Then
        Begin
             KeyCode:=kbNull;
             exit;
        End;
     End;

     Position:=GetCursorPos;

     If ((Position<0)Or(Position>length(FEditMask))) Then
     Begin
         keycode:=kbNull;
         exit;
     End;

     If keycode=kbCLeft Then
     Begin
again:
          CurrentMask:=GetCurrentMask(Position,UpLowCase,Blanks);
          If CurrentMask=Nil Then
          Begin
              keycode:=kbNull;
              exit;
          End;

          Case CurrentMask^[0] Of
            mMskAlpha,mMskAlphaOpt,mMskAlphaNum,mMskAlphaNumOpt,
            mMskAscii,mMskAsciiOpt,mMskNumeric,mMskNumericOpt,mMskNumSymOpt:;
            Else If Position>0 Then
            Begin
                 {$IFDEF OS2}
                 WinSendMsg(Handle,WM_CHAR,KC_VIRTUALKEY OR KC_SCANCODE OR KC_TOGGLE,
                             224 Or (VK_LEFT SHL 16));
                 {$ENDIF}
                 Pos1:=GetCursorPos;
                 If Pos1<>Position Then If Pos1>0 Then
                 Begin
                      Position:=Pos1;
                      goto again;
                 End;
            End;
          End;
     End;

     UpdateCursorPos;
End;

{
浜様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様�
�                                                                           �
� Speed-Pascal/2 Version 2.0                                                �
�                                                                           �
� Speed-Pascal Component Classes (SPCC)                                     �
�                                                                           �
� This section: TMaskEditTextPropertyEditor Class implementation            �
�                                                                           �
� Last modified: January 1998                                               �
�                                                                           �
� (C) 1998 SpeedSoft. All rights reserved. Disclosure probibited !          �
�                                                                           �
藩様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様�
}


Type
    TMaskEditTextPropertyEditor=Class(TStringPropertyEditor)
       Public
         Function Execute(Var Value:String;ValueLen:LONGINT):TPropertyEditorReturn;Override;
    End;

Type
    TMaskEditTextPropEditDialog=Class(TDialog)
       Procedure SetupDlg;
    End;


Procedure TMaskEditTextPropEditDialog.SetupDlg;
Begin
     Caption:='Maked Text Editor';
     Width:=420;
     Height:=400;
     XAlign:=xaCenter;
     YAlign:=yaCenter;
     Color:=clLtGray;
     BorderStyle := bsDialog;
     BorderIcons := [biSystemMenu];

     InsertBitBtn(Self,20,10,90,30,bkOk,'~Ok','Click here to accept');
     InsertBitBtn(Self,120,10,90,30,bkCancel,'~Cancel','Click here to cancel');
     InsertBitBtn(Self,220,10,90,30,bkHelp,'~Help','Click here to get help');
End;


Function TMaskEditTextPropertyEditor.Execute(Var Value:String;ValueLen:LONGINT):TPropertyEditorReturn;
VAR
    MaskEdit:TMaskEdit;
    Dlg:TMaskEditTextPropEditDialog;
Begin
     MaskEdit:=TMaskEdit(Owner);

     Dlg.Create(Nil);
     Dlg.SetupDlg;

     If Dlg.Execute Then
     Begin
          Value:='';
          result:=edOk;
     End
     Else result:=edCancel;
     Dlg.Destroy;
End;

{
浜様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様�
�                                                                           �
� Speed-Pascal/2 Version 2.0                                                �
�                                                                           �
� Speed-Pascal Component Classes (SPCC)                                     �
�                                                                           �
� This section: TMaskEditTextPropertyEditor Class implementation            �
�                                                                           �
� Last modified: January 1998                                               �
�                                                                           �
� (C) 1998 SpeedSoft. All rights reserved. Disclosure probibited !          �
�                                                                           �
藩様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様様�
}

Type
    TMaskEditMaskPropertyEditor=CLASS(TStringPropertyEditor)
       Public
         Function Execute(Var Value:String;ValueLen:LONGINT):TPropertyEditorReturn;Override;
    End;

Type
    TMaskEditMaskPropEditDialog=Class(TDialog)
       Procedure SetupDlg;
       Procedure MasksClick(Sender:TObject);
       Procedure LoadMask(Const FileName:String);
       Procedure ClearItems;
       Procedure SetEditMask(NewMask:String);
       Procedure ListBoxItemFocus(Sender:TObject;Index:LongInt);
       Procedure EditTextInvalid(Sender:TObject);
       Procedure MaskChanged(Sender:TObject);
       Procedure BlankChanged(Sender:TObject);
       Procedure LiteralChanged(Sender:TObject);

       ListBox:TListBox;
       Edit1,Edit2:TEdit;
       CheckBox:TCheckBox;
       MaskEdit:TMaskEdit;
    End;

Procedure TMaskEditMaskPropEditDialog.EditTextInvalid(Sender:TObject);
Begin
     //do nothing
End;

Procedure TMaskEditMaskPropEditDialog.LiteralChanged(Sender:TObject);
Var s:String;
    b:Byte;
    s1,s2:String;
    Blank:Char;
Begin
     s:=MaskEdit.EditMask;
     b:=pos(';',s);
     If b=0 Then exit;
     s1:=Copy(s,1,b-1);
     Delete(s,1,b);
     b:=pos(';',s);
     If b=0 Then exit;
     Delete(s,1,b);
     If CheckBox.Checked Then s1:=s1+';1'
     Else s1:=s1+';0';
     SetEditMask(s1+';'+s);
End;


Procedure TMaskEditMaskPropEditDialog.BlankChanged(Sender:TObject);
Var s:String;
    b:Byte;
    s1,s2:String;
    Blank:String;
Begin
     s:=MaskEdit.EditMask;
     b:=pos(';',s);
     If b=0 Then exit;
     s1:=Copy(s,1,b-1);
     Delete(s,1,b);
     b:=pos(';',s);
     If b=0 Then exit;
     s2:=Copy(s,1,b-1);
     If Edit2.Text='' Then Blank:=' '
     Else Blank:=Edit2.Text[1];
     SetEditMask(s1+';'+s2+';'+Blank);
End;

Procedure TMaskEditMaskPropEditDialog.SetEditMask(NewMask:String);
Begin
     Edit1.OnChange:=Nil;
     If Edit1.Text<>NewMask Then Edit1.Text:=NewMask;
     Edit1.OnChange:=MaskChanged;
     MaskEdit.EditMask:=NewMask;
     Edit2.OnChange:=Nil;
     If Edit2.Text<>MaskEdit.MaskBlank Then Edit2.Text:=MaskEdit.MaskBlank;
     Edit2.OnChange:=BlankChanged;
     CheckBox.Checked:=MaskEdit.MaskSave;
End;

Procedure TMaskEditMaskPropEditDialog.MaskChanged(Sender:TObject);
Begin
     SetEditMask(Edit1.Text);
End;


Procedure TMaskEditMaskPropEditDialog.ListBoxItemFocus(Sender:TObject;Index:LongInt);
Var p:^String;
Begin
     p:=Pointer(ListBox.Items.Objects[Index]);
     SetEditMask(p^);
End;

Procedure TMaskEditMaskPropEditDialog.ClearItems;
Var t:LongInt;
    p:^String;
Begin
     For t:=0 To ListBox.Items.Count-1 Do
     Begin
          p:=Pointer(ListBox.Items.Objects[t]);
          DisposeStr(p);
     End;
     ListBox.Items.Clear;
End;

Procedure TMaskEditMaskPropEditDialog.LoadMask(Const FileName:String);
Var f:System.Text;
    s,s1,s2:String;
    p:^String;
    Mask:TMaskEdit;
    Save:Boolean;
Begin
     System.Assign(f,FileName);
     {$I-}
     Reset(f);
     {$I+}
     If IoResult<>0 Then
     Begin
         ErrorBox('Cannot open:'+FileName);
         exit;
     End;

     ClearItems;
     Mask.Create(Self);
     While not Eof(f) Do
     Begin
          {$I-}
          Readln(f,s);
          {$I+}
          If IoResult<>0 Then
          Begin
              ErrorBox('Cannot read file:'+FileName);
              break;
          End;

          While s[length(s)]=#32 do dec(s[0]);
          If s='' Then continue;

          If pos(';',s)=0 Then
          Begin
               ErrorBox('Illegal file format:'+FileName);
               break;
          End;

          s1:=Copy(s,1,pos(';',s)-1);
          Delete(s,1,pos(';',s));

          If pos(';',s)=0 Then
          Begin
               ErrorBox('Illegal file format:'+FileName);
               break;
          End;

          s2:=Copy(s,1,pos(';',s)-1);
          Delete(s,1,pos(';',s));

          Mask.EditMask:=s;
          p:=Nil;
          AssignStr(p,s);

          Save:=Mask.FMaskSave;
          Mask.FMaskSave:=False;
          Mask.Text:=s2;
          Mask.FMaskSave:=True;
          s2:=Mask.Text;
          Mask.FMaskSave:=Save;
          ListBox.Items.AddObject(s1+' ('+s2+')',TObject(p));
     End;
     Mask.Destroy;

     {$I-}
     System.Close(f);
     {$I+}
End;

Procedure TMaskEditMaskPropEditDialog.MasksClick(Sender:TObject);
Var OpenDialog:TOpenDialog;
    Dir,Name,Ext:String;
    OldDir:String;
Begin
     OpenDialog.Create(Nil);
     OpenDialog.AddFilter('Edit Masks','*.msk');

     FSplit(ParamStr(0),Dir,Name,Ext);
     If Dir[length(Dir)]='\' Then dec(Dir[0]);

     GetDir(0,OldDir);
     {$I-}
     ChDir(Dir);
     {$I+}

     OpenDialog.FileName:=Dir+'\*.msk';
     If OpenDialog.Execute Then LoadMask(OpenDialog.FileName);

     {$I-}
     ChDir(OldDir);
     {$I+}
End;

Procedure TMaskEditMaskPropEditDialog.SetupDlg;
Var Button:TButton;
    Dir,Name,Ext:String;
    GroupBox:TGroupBox;
Begin
     Caption:='EditMask Editor';
     Width:=460;
     Height:=350;
     XAlign:=xaCenter;
     YAlign:=yaCenter;
     Color:=clLtGray;
     BorderStyle := bsDialog;
     BorderIcons := [biSystemMenu];

     Button:=InsertButton(Self,20,10,90,30,'~Masks...','Click here to load masks');
     Button.OnClick:=MasksClick;

     InsertLabel(Self,200,275,100,20,'Sample Masks');
     ListBox:=InsertListBox(Self,200,60,240,215,'');
     ListBox.OnItemFocus:=ListBoxItemFocus;

     InsertLabel(Self,15,275,100,20,'Input Mask');
     Edit1:=InsertEdit(Self,15,250,170,20,'','');
     Edit1.OnChange:=MaskChanged;

     InsertLabel(Self,15,220,120,20,'Character for blanks:');
     Edit2:=InsertEdit(Self,145,220,30,20,'','');
     Edit2.OnChange:=BlankChanged;

     CheckBox:=InsertCheckBox(Self,15,190,160,20,'Save literal characters','');
     CheckBox.OnClick:=LiteralChanged;

     GroupBox:=InsertGroupBox(Self,15,130,170,50,'Test Input');
     MaskEdit:=InsertMaskEdit(GroupBox,10,10,150,20,'','');
     MaskEdit.OnEditTextInvalid:=EditTextInvalid;

     InsertBitBtn(Self,120,10,90,30,bkOk,'~Ok','Click here to accept');
     InsertBitBtn(Self,220,10,90,30,bkCancel,'~Cancel','Click here to cancel');
     InsertBitBtn(Self,320,10,90,30,bkHelp,'~Help','Click here to get help');

     FSplit(ParamStr(0),Dir,Name,Ext);
     If Dir[length(Dir)]='\' Then dec(Dir[0]);
     LoadMask(Dir+'\Germany.msk');
End;


Function TMaskEditMaskPropertyEditor.Execute(Var Value:String;ValueLen:LONGINT):TPropertyEditorReturn;
VAR
    MaskEdit:TMaskEdit;
    Dlg:TMaskEditMaskPropEditDialog;
Begin
     MaskEdit:=TMaskEdit(Owner);

     Dlg.Create(Nil);
     Dlg.SetupDlg;
     Dlg.SetEditMask(MaskEdit.EditMask);

     If Dlg.Execute Then
     Begin
          Value:=Dlg.MaskEdit.EditMask;
          result:=edOk;
     End
     Else result:=edCancel;
     Dlg.ClearItems;
     Dlg.Destroy;
End;


Initialization
   AddPropertyEditor(TMaskEdit,'EditMask',TMaskEditMaskPropertyEditor);
   AddPropertyEditor(TMaskEdit,'Text',TMaskEditTextPropertyEditor);
End.
