Patch from David Guadagnini plus fix from Junior Goncalves and other

fixes from myself. Implements maskedit.pp

git-svn-id: trunk@14760 -
This commit is contained in:
sekelsenmat 2008-04-05 13:39:50 +00:00
parent e523d1cf32
commit 78c6aa38dd

View File

@ -3,8 +3,6 @@
maskedit.pp
-----------
Component Library Code
Does not yet support charsets that use multiple bytes per char
***************************************************************************/
@ -21,19 +19,6 @@
* *
*****************************************************************************
}
{ ***************************************************************************** }
{ ***************************************************************************** }
{ ***************************************************************************** }
{ ***************************************************************************** }
{
MWE: Code removed since almost all was copyrighted material.
}
{ ***************************************************************************** }
{ ***************************************************************************** }
{ ***************************************************************************** }
{ ***************************************************************************** }
unit MaskEdit;
{$mode objfpc}{$H+}
@ -41,108 +26,216 @@ unit MaskEdit;
interface
uses
Classes, SysUtils, StdCtrls, Controls, LMessages, LCLType, Graphics;
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ExtCtrls,
StdCtrls, LMessages, Clipbrd, LCLType, Windows;
const
{ Mask Type }
cMask_SpecialChar = '\'; // after this you can set an arbitrary char
cMask_UpperCase = '>'; // after this the chars is in upper case
cMask_LowerCase = '<'; // after this the chars is in lower case
cMask_Letter = 'L'; // only a letter but not necessary
cMask_LetterFixed = 'l'; // only a letter
cMask_AllChars = 'A'; // a char from space and #122 but not necessary
cMask_AllCharsFixed = 'a'; // a char from space and #122
cMask_Number = '0'; // only a number but not necessary
cMask_NumberFixed = '9'; // only a number
cMask_HourSeparator = ';'; // automatically put the hour separator char
cMask_DateSeparator = '/'; // automatically but the date separator char
cMask_SpaceOnly = '_'; // automatically put a space
type
{ Type for mask (internal) }
tMaskedType = (Char_Start,
Char_Number,
Char_NumberFixed,
Char_Letter,
Char_LetterFixed,
Char_LetterUpCase,
Char_LetterDownCase,
Char_LetterFixedUpCase,
Char_LetterFixedDownCase,
Char_All,
Char_AllFixed,
Char_AllUpCase,
Char_AllDownCase,
Char_AllFixedUpCase,
Char_AllFixedDownCase,
Char_Space,
Char_HourSeparator,
Char_DateSeparator,
Char_Stop);
{ Exception class }
EDBEditError = class(Exception);
TEditMask = type string;
{ TCustomMaskEdit }
TCustomMaskEdit = Class(TCustomEdit)
private
FPosition : Integer; // Current position
FRealMask : String; // Real mask inserted
FMask : ShortString; // Acrtual internal mask
FMaxChars : Integer; // Max writeable chars
FSpaceChar : Char; // Char for space (default '_')
CurrentText : String; // Current text
TCustomMaskEdit = class(TCustomEdit)
private
FEditMask: TEditMask;
procedure SetMask(Value : String);
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
procedure CMEnter(var Message: TMessage); message CM_ENTER;
procedure LMMButtonUp(var Message: TLMMButtonDown); message LM_MBUTTONUP;
procedure LMPasteFromClip(var Message: TMessage); message LM_PASTE;
procedure LMCutToClip(var Message: TMessage); message LM_CUT;
procedure LMClearSel(var Message : TMessage); message LM_CLEAR;
function ClearChar(Position : Integer) : Char;
procedure SetCursor;
function GetIsMasked : Boolean;
procedure InsertChar(Ch : Char);
procedure DeleteSelected(AlsoOnePosition : Boolean);
procedure SetCharToPos;
procedure DeleteChars(NextChar : Boolean);
Function GetText : String;
Procedure SetText(Value : String);
Function CanInsertChar(Position : Integer; Var Ch : Char) : Boolean;
procedure SetSpaceChar(Value : Char);
Function CharToMask(Ch : Char) : tMaskedType;
Function MaskToChar(Value : tMaskedType) : Char;
Function IsMaskChar(Ch : Char) : Boolean;
Function SearchDeletedText : Boolean;
procedure SetEditText(const AValue: string);
protected
{ Required methods }
function EditCanModify: Boolean; virtual;
function GetEditText: string; virtual;
procedure Reset; virtual;
property EditMask: TEditMask read FEditMask write FEditMask;
public
procedure ValidateEdit; virtual;
property EditText: string read GetEditText write SetEditText;
end;
{ TMaskEdit }
procedure Clear;
constructor Create(Aowner : TComponent); override;
procedure GetSel(var _SelStart: Integer; var _SelStop: Integer);
procedure SetSel(_SelStart: Integer; _SelStop: Integer);
{ Required methods }
procedure ValidateEdit; virtual;
property EditMask : string read FRealMask write SetMask;
property isMasked : Boolean read GetIsMasked;
property Text : string read GetText write SetText;
property EditText : string read GetEditText write SetEditText;
property SpaceChar : Char read FSpaceChar write SetSpaceChar;
end;
{ TMaskEdit }
TMaskEdit = class(TCustomMaskEdit)
published
property Align;
property Anchors;
property AutoSize;
property AutoSelect;
property BorderSpacing;
property AutoSize;
property BiDiMode;
property BorderStyle;
property CharCase;
property Color;
property Constraints;
property CharCase;
property Ctl3D;
property DragCursor;
property DragKind;
property DragMode;
property EchoMode;
property EditMask;
property Enabled;
property Font;
property MaxLength;
property OnChange;
property OnChangeBounds;
property OnClick;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEditingDone;
property OnEndDrag;
property OnEnter;
property OnExit;
Property OnKeyDown;
property OnKeyPress;
Property OnKeyUp;
Property OnMouseDown;
Property OnMouseMove;
property OnMouseUp;
property OnResize;
property OnStartDrag;
property ParentBiDiMode;
property ParentColor;
property ParentCtl3D;
property ParentFont;
property ParentShowHint;
property PasswordChar;
property PopupMenu;
property ReadOnly;
property ShowHint;
property TabStop;
property TabOrder;
property Text;
property TabStop;
property Visible;
end;
procedure Register;
property OnChange;
property OnClick;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEndDock;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnStartDock;
property OnStartDrag;
property EditMask;
property isMasked;
property Text;
property SpaceChar;
end;
procedure Register;
implementation
{ Component registration procedure }
procedure Register;
begin
// RegisterComponents('Additional',[TMaskEdit]);
RegisterComponents('Additional',[TMaskEdit]);
end;
function TCustomMaskEdit.EditCanModify: Boolean;
// Respond to Clear message
procedure TCustomMaskEdit.LMClearSel(var Message: TMessage);
begin
Result := True;
if Not SearchDeletedText then inherited;
end;
function TCustomMaskEdit.GetEditText: string;
// Respond to Cut message
procedure TCustomMaskEdit.LMCutToClip(var Message: TMessage);
begin
Result := Text;
if Not SearchDeletedText then inherited;
end;
procedure TCustomMaskEdit.Reset;
// Search and init the deleted text
function TCustomMaskEdit.SearchDeletedText : Boolean;
var
S : String;
NLose, I : Integer;
StartPosition : Integer;
Ok : Boolean;
begin
// Number of char deleted
NLose := Length(CurrentText)-Length(inherited Text);
// Search part deleted
Ok := False;
StartPosition := 1;
While (Not Ok) And (StartPosition+NLose-1 <= Length(CurrentText)) Do
begin
S := CurrentText;
Delete(S, StartPosition, NLose);
Ok := (S = (Inherited Text));
if Not Ok then Inc(StartPosition);
end;
// Found! Reinsert chars
if Ok then
begin
for I := StartPosition to StartPosition+NLose-1 do
begin
CurrentText[I] := ClearChar(I);
end;
Inherited Text := CurrentText;
SetCursor;
Result := True;
end
else
Result := False;
end;
procedure TCustomMaskEdit.SetEditText(const AValue: string);
@ -150,8 +243,627 @@ begin
Text := AValue;
end;
procedure TCustomMaskEdit.ValidateEdit;
function TCustomMaskEdit.EditCanModify: Boolean;
begin
Result := True;
end;
function TCustomMaskEdit.GetEditText: string;
begin
Result := Text;
end;
procedure TCustomMaskEdit.Reset;
begin
end;
// Respond to Paste message
procedure TCustomMaskEdit.LMPasteFromClip(var Message: TMessage);
Var
NewText : String;
begin
if (not IsMasked) or (ReadOnly) then
begin
inHerited;
Exit;
end;
// Save the current text
NewText := InHerited Text;
// Put the stored text into the control
InHerited Text := CurrentText;
// Try to set the new text
Text := NewText;
end;
Function TCustomMaskEdit.CanInsertChar(Position : Integer; Var Ch : Char) : Boolean;
Var
Current : tMaskedType;
Begin
Current := CharToMask(FMask[Position]);
Result := False;
// If in UpCase convert the input char
if (Current = Char_LetterUpCase ) Or
(Current = Char_LetterFixedUpCase) Or
(Current = Char_AllUpCase ) Or
(Current = Char_AllFixedUpCase )
then
Ch := UpCase(Ch);
// If in LowerCase convert the input char
if (Current = Char_LetterDownCase ) Or
(Current = Char_LetterFixedDownCase) Or
(Current = Char_AllDownCase ) Or
(Current = Char_AllFixedDownCase )
then
Ch := LowerCase(Ch);
// Check the input (check the valid range)
case Current Of
Char_Number : Result := Ch In ['0'..'9'];
Char_NumberFixed : Result := Ch In ['0'..'9'];
Char_Letter : Result := Ch In ['a'..'z', 'A'..'Z'];
Char_LetterFixed : Result := Ch In ['a'..'z', 'A'..'Z'];
Char_LetterUpCase : Result := Ch In ['A'..'Z'];
Char_LetterDownCase : Result := Ch In ['a'..'z'];
Char_LetterFixedUpCase : Result := Ch In ['A'..'Z'];
Char_LetterFixedDownCase : Result := Ch In ['a'..'z'];
Char_All : Result := True;
Char_AllFixed : Result := True;
Char_AllUpCase : Result := True;
Char_AllDownCase : Result := True;
Char_AllFixedUpCase : Result := True;
Char_AllFixedDownCase : Result := True;
end;
end;
// Insert a single char in position Fposition
procedure TCustomMaskEdit.InsertChar(Ch : Char);
Var
Ok : Boolean;
I, C : Integer;
S : ShortString;
begin
// Search the correct char writeable
Ok := False;
I := 1;
C := 0;
while (Not Ok) And (I <= Length(FMask)) do
begin
if IsMaskChar(FMask[I]) then
begin
Inc(C);
Ok := (C = FPosition);
end;
if Not Ok then Inc(I);
end;
// found!
if Ok then
// Char is valid. Put the char into Text
if CanInsertChar(I, Ch) then
begin
S := Text;
S[I] := Ch;
Inherited Text := S;
CurrentText := S;
Inc(FPosition);
SetCursor;
end;
end;
// Respond to Mouse Button Up message
procedure TCustomMaskEdit.LMMButtonUp(var Message: TLMMButtonDown);
begin
InHerited;
if isMasked then
begin
FPosition := 0;
SetCharToPos;
SetCursor;
end;
end;
// Respond to Enter message
procedure TCustomMaskEdit.CMEnter(var Message: TMessage);
begin
InHerited;
if isMasked then
begin
FPosition := 0;
SetCharToPos;
SetCursor;
end;
end;
// Create object
constructor TCustomMaskEdit.Create(Aowner : TComponent);
begin
Inherited Create(Aowner);
FPosition := 1;
FRealMask := '';
FMask := '';
FMaxChars := 0;
FSpaceChar := '_';
CurrentText := Inherited Text;
end;
// Set the current Space Char
procedure TCustomMaskEdit.SetSpaceChar(Value : Char);
Var
S, Old : ShortString;
I : Integer;
Begin
if (Value <> FSpaceChar) And (Not IsMaskChar(Value)) then
begin
Old := FSpaceChar;
FSpaceChar := Value;
if isMasked then
begin
S := Text;
for I := 1 to Length(S) do
if (CharToMask(FMask[I]) = Char_Space)
then
S[I] := FSpaceChar;
Inherited Text := S;
CurrentText := S;
end;
end;
End;
// Get the current selection
procedure TCustomMaskEdit.GetSel(var _SelStart: Integer; var _SelStop: Integer);
begin
SendMessage(Handle, EM_GETSEL, Integer(@_SelStart), Integer(@_SelStop));
end;
// Set the current selection
procedure TCustomMaskEdit.SetSel(_SelStart: Integer; _SelStop: Integer);
begin
SendMessage(Handle, EM_SETSEL, _SelStart, _SelStop);
end;
procedure TCustomMaskEdit.ValidateEdit;
begin
end;
// Respond to Text Changed message
procedure TCustomMaskEdit.CMTextChanged(var Message: TMessage);
begin
InHerited;
if Text = '' then Clear;
end;
// Single key down procedure
procedure TCustomMaskEdit.KeyDown(var Key: Word; Shift: TShiftState);
begin
// Not masked or shift pressed -> old procedure
if (Not isMasked) Or (ReadOnly) Or (ssCTRL in Shift) then
begin
InHerited KeyDown(Key, Shift);
Exit;
end;
// Cursor movement
if (Key = VK_LEFT) or (Key = VK_RIGHT) then
begin
if Not (ssShift in Shift) then
begin
SetCharToPos;
if Key = VK_LEFT then Dec(FPosition)
else Inc(FPosition);
Key := 0;
SetCursor;
end;
Exit;
end;
// Cursor position to end or to begin
if (Key = VK_HOME) or (Key = VK_end) then
begin
if Key = VK_HOME then FPosition := 1
else FPosition := FMaxChars-1;
SetCursor;
Key := 0;
Exit;
end;
// Cursor Up/Down -> not valid
if (Key = VK_UP) or (Key = VK_DOWN) then
begin
Key := 0;
Exit;
end;
// Delete a single char
if (Key = VK_BACK) or (Key = VK_DELETE) then
begin
DeleteChars(Key = VK_DELETE);
Key := 0;
Exit;
end;
// Insert a char
if (Key In [32..122]) then
begin
DeleteSelected(False);
SetCharToPos;
InsertChar(Char(Lo(Key)));
Key := 0;
end;
end;
// Delete a single char from position
procedure TCustomMaskEdit.DeleteChars(NextChar : Boolean);
Var
SelectionStart, SelectionStop : Integer;
begin
GetSel(SelectionStart, SelectionStop);
// Must delete the next char from actual position
if Not NextChar then
begin
Dec(FPosition);
SetSel(SelectionStart-1, SelectionStop-1);
SetCursor;
end;
DeleteSelected(True);
end;
// Return if mask is selected
function TCustomMaskEdit.GetIsMasked : Boolean;
begin
Result := (FMask <> '') And (FMaxChars > 0);
end;
// Set the cursor position
procedure TCustomMaskEdit.SetCursor;
Var
Ok : Boolean;
I, C : Integer;
begin
if FPosition < 1 then FPosition := 1;
if FPosition > FMaxChars then FPosition := FMaxChars;
Ok := False;
I := 1;
C := 0;
while (Not Ok) And (I <= Length(FMask)) do
begin
if IsMaskChar(FMask[I]) then
begin
Inc(C);
Ok := (C = FPosition);
end;
if Not Ok then Inc(I);
end;
if Ok then SetSel(I-1, I-1);
end;
// Clear (virtually) a single char in position Position
function TCustomMaskEdit.ClearChar(Position : Integer) : Char;
begin
Result := FMask[Position];
case CharToMask(FMask[Position]) Of
Char_Number : Result := FSpaceChar;
Char_NumberFixed : Result := '0';
Char_Letter : Result := FSpaceChar;
Char_LetterFixed : Result := 'a';
Char_LetterUpCase : Result := FSpaceChar;
Char_LetterDownCase : Result := FSpaceChar;
Char_LetterFixedUpCase : Result := 'A';
Char_LetterFixedDownCase : Result := 'a';
Char_All : Result := FSpaceChar;
Char_AllFixed : Result := '0';
Char_AllUpCase : Result := FSpaceChar;
Char_AllDownCase : Result := FSpaceChar;
Char_AllFixedUpCase : Result := '0';
Char_AllFixedDownCase : Result := '0';
Char_Space : Result := FSpaceChar;
Char_HourSeparator : Result := DecimalSeparator;
Char_DateSeparator : Result := DateSeparator;
end;
end;
// Clear all string
procedure TCustomMaskEdit.Clear;
Var
S : ShortString;
I : Integer;
begin
Inherited Clear;
if isMasked then
begin
S := '';
for I := 1 To Length(FMask) do S := S + ClearChar(I);
inherited Text := S;
CurrentText := S;
FPosition := 0;
SetCursor;
end;
end;
// Prepare the real Mask
procedure TCustomMaskEdit.SetMask(Value : String);
Var
S : ShortString;
I : Integer;
InUp, InDown : Boolean;
Special : Boolean;
begin
if FRealMask <> Value then
begin
// init
FMaxChars := 0;
FRealMask := Value;
FMask := '';
// Init: No UpCase, No LowerCase, No Special Char
InUp := False;
InDown := False;
Special := False;
// Get Actual Mask
S := FRealMask;
for I := 1 To Length(S) do
begin
// Must insert a special char
if Special then
begin
FMask := FMask + S[I];
Special := False;
end
else
begin
Inc(FMaxChars);
// Check the char to insert
case S[I] Of
cMask_SpecialChar: Special := True;
cMask_UpperCase: begin
InUp := True;
if InDown then
begin
InUp := False;
InDown := False;
end;
end;
cMask_LowerCase: begin
InDown := True;
if InUp then
begin
InUp := False;
InDown := False;
end;
end;
cMask_Letter: begin
if InUp
then
FMask := FMask + MaskToChar(Char_LetterUpCase)
else
if InDown
then
FMask := FMask + MaskToChar(Char_LetterDownCase)
else
FMask := FMask + MaskToChar(Char_Letter)
end;
cMask_LetterFixed: begin
if InUp
then
FMask := FMask + MaskToChar(Char_LetterFixedUpCase)
else
if InDown
then
FMask := FMask + MaskToChar(Char_LetterFixedDownCase)
else
FMask := FMask + MaskToChar(Char_LetterFixed)
end;
cMask_AllChars: begin
if InUp
then
FMask := FMask + MaskToChar(Char_AllUpCase)
else
if InDown
then
FMask := FMask + MaskToChar(Char_AllDownCase)
else
FMask := FMask + MaskToChar(Char_All)
end;
cMask_AllCharsFixed: begin
if InUp
then
FMask := FMask + MaskToChar(Char_AllFixedUpCase)
else
if InDown
then
FMask := FMask + MaskToChar(Char_AllFixedDownCase)
else
FMask := FMask + MaskToChar(Char_AllFixed)
end;
cMask_Number: FMask := FMask + MaskToChar(Char_Number);
cMask_NumberFixed: FMask := FMask + MaskToChar(Char_NumberFixed);
cMask_HourSeparator: FMask := FMask + MaskToChar(Char_HourSeparator);
cMask_DateSeparator: FMask := FMask + MaskToChar(Char_DateSeparator);
cMask_SpaceOnly: FMask := FMask + MaskToChar(Char_Space);
else begin
FMask := FMask + S[I];
Dec(FMaxChars);
end;
end;
end;
end;
Clear;
end;
end;
// Trasform a position from Text in a real position
procedure TCustomMaskEdit.SetCharToPos;
Var
SelectionStart, SelectionStop : Integer;
I, Last, A, B : Integer;
begin
// Search for the position of insertion valid nearest the cursor
GetSel(SelectionStart, SelectionStop);
if IsMaskChar(FMask[SelectionStart+1]) then
begin
Last := SelectionStart+1;
end
else
begin
I := SelectionStart+1;
A := -999;
while (A < 0) And (I <= Length(FMask)) do
begin
if IsMaskChar(FMask[I]) then A := I;
Inc(I);
end;
I := SelectionStart-1;
B := -999;
while (B < 0) And (I >= 1) do
begin
if IsMaskChar(FMask[I]) then B := I;
Dec(I);
end;
Last := B;
if (A-SelectionStart) <= (SelectionStart-B) then Last := A;
end;
A := 0;
for I := 1 To Last do
if IsMaskChar(FMask[I]) then Inc(A);
FPosition := A;
SetCursor;
end;
// Delete selected chars
procedure TCustomMaskEdit.DeleteSelected(AlsoOnePosition : Boolean);
Var
SelectionStart, SelectionStop, I : Integer;
S : ShortString;
begin
GetSel(SelectionStart, SelectionStop);
if (SelectionStop > SelectionStart ) Or
((SelectionStop = SelectionStart) And AlsoOnePosition) then
begin
S := Text;
for I := SelectionStart+1 To SelectionStop+1 do S[I] := ClearChar(I);
inherited Text := S;
CurrentText := S;
end;
SetSel(SelectionStart, SelectionStart);
end;
// Get the actual Text
Function TCustomMaskEdit.GetText : String;
Begin
Result := InHerited Text;
End;
// Set the actual Text
Procedure TCustomMaskEdit.SetText(Value : String);
Var
Old, S, S1 : ShortString;
I : Integer;
Ok : Boolean;
Begin
if isMasked Then
begin
if Value <> '' then
begin
Old := Text;
S := Value;
S1 := '';
I := 1;
Ok := (Length(S) = Length(FMask));
while (I <= Length(S)) and (Ok) do
begin
if Not IsMaskChar(FMask[I])
then
Ok := (S[I] = FMask[I])
else
Ok := CanInsertChar(I, S[I]);
if OK then S1 := S1 + S[I];
Inc(I);
end;
if not Ok then
begin
inherited Text := Old;
CurrentText := Old;
raise EDBEditError.Create('Error setting text...!');
end
else
begin
inherited Text := S1;
CurrentText := S1;
end;
end
else
Clear;
end
else
begin
inherited Text := Value;
CurrentText := Value;
end;
End;
// Trasform a single char in a MaskType
Function TCustomMaskEdit.CharToMask(Ch : Char) : tMaskedType;
Begin
Result := Char_Start;
if (Ord(Ch) > Ord(Char_Start)) and
(Ord(Ch) < Ord(Char_Stop) )
then
Result := tMaskedType(Ord(Ch));
End;
// Trasform a single MaskType into a char
Function TCustomMaskEdit.MaskToChar(Value : tMaskedType) : Char;
Begin
Result := Char(Ord(Value));
End;
// Return if the char passed is a valid MaskType char
Function TCustomMaskEdit.IsMaskChar(Ch : Char) : Boolean;
Begin
Result := (CharToMask(Ch) <> Char_Start);
End;
end.