Jump to content

new date component


jahlxx

Recommended Posts

Hi.

 

In my vcl apps, I use a component named TXDateEdit. The code is below.

 

Is very useful for me, and I'd like to use it in my unigui apps.

 

Can anyone help me to convert it to a new unigui component?

 

Thanks in advance.

 

This is the code:

 

 

 

unit DateEdit;

interface

uses
  Windows, SysUtils, Messages, Classes, Controls, Forms, StdCtrls, Graphics;

type
  TCheckDateEvent =
    function(Sender: TObject; Date: TDateTime): boolean of object;

  TXDateEdit = class(TCustomEdit)
  private
    FLastValidDate: TDateTime;
    FUseOSDateFormat: boolean;
    FDateFormat: string;
    fEpoch: integer;
    FBlankDate: string;
    FColorOK, FColorInvalid: TColor;
    fZeroDateIsValid: boolean;
    fChangingColorOnly: boolean;
    FDoCheckDate: TCheckDateEvent;
    FDoInvalidDate: TNotifyEvent;
    procedure SetUseOSDateFormat(value: boolean);
    function GetDisplayedDate(out dt: TDateTime): boolean;
    procedure AdjustWidth;
    procedure ReformatText;
    function GetDateFormat: string;
    procedure SetDateFormat(const value: string);
    function GetDate: TDateTime;
    procedure SetDate(NewDate: TDateTime);
    procedure SetEpoch(value: integer);
    procedure CMExit(var Message: TCMExit); message CM_EXIT;
    procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  protected
    function DoCheckDate(Date: TDatetime): boolean; virtual;
    procedure CreateWnd; override;
    procedure KeyPress(var Key: Char); override;
    procedure Change; override;
  public
    function IsValidDate: boolean;
    constructor Create(AOwner: TComponent); override;
    property Date: TDateTime read GetDate write SetDate;
    procedure Clear; override;
    property Epoch: integer read fEpoch write SetEpoch; //see history 190503
  published
    property AutoSelect;
    property AutoSize;
    property BorderStyle;
    property Color;
    property ColorTextErr: TColor read fColorInvalid write fColorInvalid;
    property ColorTextOK: TColor read fColorOK write fColorOK;
    property Ctl3D;
    //DateFormat: can only be assigned when UseOSDateFormat property = false
    property DateFormat: string read GetDateFormat write SetDateFormat;
    property Enabled;
    property Font;
    property HideSelection;
    property ParentColor;
    property ParentCtl3D;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ReadOnly;
    property ShowHint;
    property TabOrder;
//    property Text: string read GetText write SetText;
    property UseOSDateFormat: boolean
      read FUseOSDateFormat write SetUseOSDateFormat;
    property Visible;
    property ZeroDateIsValid: boolean
      read fZeroDateIsValid write fZeroDateIsValid;
    //OnCheckDate: optional event to facilitate restricting the date range(s)
    property OnCheckDate: TCheckDateEvent
      read FDoCheckDate write FDoCheckDate; //see history 310503
      
    //OnInvalidDate: optional event to customize invalid date notification
    property OnInvalidDate: TNotifyEvent
      read FDoInvalidDate write FDoInvalidDate;
    property OnChange;
    property OnClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
  end;

procedure Register;

implementation

resourcestring
  s_invalid_date = 'Invalid Date: ';
  s_invalid_integer = 'Invalid Integer';

//------------------------------------------------------------------------------
// Miscellaneous functions ...
//------------------------------------------------------------------------------

procedure Register;
begin
  RegisterComponents('Samples', [TXDateEdit]);
end;
//------------------------------------------------------------------------------

function strToWord(const str: string; startPos, len: integer): word;
var
  i: integer;
begin
  if (length(str) < startPos + len -1) or (len < 1) or (startPos < 1) then
    raise Exception.Create(s_invalid_integer);
  result := 0;
  for i := 1 to len do
  begin
    if not (str[startPos] in ['0'..'9']) then
      raise Exception.Create(s_invalid_integer);
    result := (result*10) + ord(str[startPos]) - ord('0');
    inc(startPos);
  end;
end;
//------------------------------------------------------------------------------

{$IFDEF VER100}
//Unfortunatley, in Delphi3, the DoEncodeDate() function only has unit scope.
function TryEncodeDate(Year, Month, Day: Word; out Date: TDateTime): Boolean;
var
  I: Integer;
  DayTable: PDayTable;
begin
  Result := False;
  DayTable := @MonthDays[isLeapYear(Year)];
  if (Year >= 1) and (Year <= 9999) and (Month >= 1) and (Month <= 12) and
    (Day >= 1) and (Day <= DayTable^[Month]) then
  begin
    for I := 1 to Month - 1 do Inc(Day, DayTable^);
    I := Year - 1;
    Date := I * 365 + I div 4 - I div 100 + I div 400 + Day - DateDelta;
    Result := True;
  end;
end;
{$ENDIF}

//------------------------------------------------------------------------------
// TXDateEdit methods ...
//------------------------------------------------------------------------------

constructor TXDateEdit.Create(AOwner: TComponent);
var
  yy,mm,dd: word;
begin
  inherited Create(AOwner);
  fZeroDateIsValid := true;
  //FColorInvalid := clRed;
  FColorInvalid := clBlack;
  FColorOK := clBlack;
  DecodeDate(sysUtils.Date,yy,mm,dd);
  //Epoch is only read when dates are entered without a specified century.
  //eg if Epoch = 1920 then entering the date 1/1/20 will be rendered to
  //01/01/1920 and the date 1/1/19 will be rendered to 01/01/2019.
  fEpoch := round(yy /10)*10 - 80;
  SetDateFormat(formatsettings.ShortDateFormat);
  FUseOSDateFormat := true;
//  SetDate(sysUtils.date);
end;
//------------------------------------------------------------------------------

procedure TXDateEdit.SetEpoch(value: integer);
begin
  if (value < 1900) or (value > 2100) then exit;
  fEpoch := value;
end;
//------------------------------------------------------------------------------

procedure TXDateEdit.Clear;
begin
  //simply restore the last valid date
  ReformatText;
end;
//------------------------------------------------------------------------------

function TXDateEdit.GetDateFormat: string;
begin
  if FUseOSDateFormat then
    result := '' else
    result := FDateFormat;
end;
//------------------------------------------------------------------------------

procedure TXDateEdit.SetDateFormat(const value: string);
var
  ch: char;
begin
  //if UseOSDateFormat = true then don't allow resetting of fDateFormat ...
  if not (csLoading in ComponentState) and FUseOSDateFormat then exit;
  if value = '' then ch := 'Y'
  else ch := upcase(value[1]);
  case ch of
    'D': FDateFormat := 'dd/mm/yyyy';
    'M': FDateFormat := 'mm/dd/yyyy';
    else FDateFormat := 'yyyy/mm/dd';
  end;
  if FDateFormat[1] = 'y' then
    FBlankDate := format('0000%s00%0:s00',[formatsettings.dateSeparator]) else
    FBlankDate := format('00%s00%0:s0000',[formatsettings.dateSeparator]);
  ReformatText;
  AdjustWidth;
end;
//------------------------------------------------------------------------------

procedure TXDateEdit.ReformatText;
var
  dd,mm,yy: word;
begin
  if (FLastValidDate = 0) then
  begin
    if (Owner is TCustomForm) and (TCustomForm(Owner).ActiveControl = self) then
      inherited text := FBlankDate else
      inherited text := '';
  end else
  begin
    DecodeDate(FLastValidDate,yy,mm,dd);

    case FDateFormat[1] of
      'd': inherited Text :=
        Format('%2.2d%s%2.2d%1:s%3:4.4d',[dd,formatsettings.DateSeparator,mm,yy]);
      'm': inherited Text :=
        Format('%2.2d%s%2.2d%1:s%3:4.4d',[mm,formatsettings.DateSeparator,dd,yy]);
      else inherited Text :=
        Format('%4.4d%s%2.2d%1:s%3:2.2d',[yy,formatsettings.DateSeparator,mm,dd]);
    end;
  end;
end;
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
function TXDateEdit.GetDisplayedDate(out dt: TDateTime): boolean;
var
  dd,mm,yy: word;
  i, len: integer;
  hasCentury: boolean;
  tmpSep1, tmpSep2: integer;
begin
  result := false;
  len := length(text);
  if (len = 0) then
  begin
    result := fZeroDateIsValid;
    dt := 0;
    exit;
  end;
  if len > 10 then exit;
  tmpSep1 := 0; tmpSep2 := 0;
  //validate the position of the date separators, if any, and
  //make sure only date separators and numerics are entered ...
  for i := 1 to len do
    if text = formatsettings.dateSeparator then
    begin
      if i = 1 then exit
      else if tmpSep1 = 0 then tmpSep1 := i
      else if (i = tmpSep1+1) or (tmpSep2 > 0) then exit
      else tmpSep2 := i;
    end else
      if not (text in ['0'..'9']) then exit;

  //check for other error conditions ...
  if ((tmpSep1 = 0) and not (len in [6,8])) or
    ((tmpSep1 > 0) and (tmpSep2 = 0)) or
    ((tmpSep1 > 0) and (tmpSep2 - tmpSep1 > 3)) or (tmpSep2 = len) then exit;

  hasCentury :=
    ((tmpSep1 = 0) and (len = 8)) or //ddmmyyyy
    ((tmpSep1 = 5) and (FDateFormat[1] = 'y')) or //yyyy/mm/dd
    ((FDateFormat[1] <> 'y') and (tmpSep2 = len-4)); //dd/mm/yyyy

  try
    case FDateFormat[1] of
      'y': if (tmpSep1 > 0) then
           begin
             if tmpSep1 = 4 then exit; //must be either y, yy or yyyy
             yy := strToWord(text,1,tmpSep1-1);
             mm := strToWord(text,tmpSep1+1,tmpSep2-tmpSep1-1);
             dd := strToWord(text,tmpSep2+1,len-tmpSep2);
           end else
           begin
             if hasCentury then i := 3 else i := 5;
             yy := strToWord(text,1,i-1);
             mm := strToWord(text,i,2);
             dd := strToWord(text,i+2,2);
           end;
      'd': if (tmpSep1 > 0) then
           begin
             if tmpSep2 = len - 3 then exit; //must be either y, yy or yyyy
             dd := strToWord(text,1,tmpSep1-1);
             mm := strToWord(text,tmpSep1+1,tmpSep2-tmpSep1-1);
             yy := strToWord(text,tmpSep2+1,len-tmpSep2);
           end else
           begin
             dd := strToWord(text,1,2);
             mm := strToWord(text,3,2);
             yy := strToWord(text,5,len-4);
           end;
      else if (tmpSep1 > 0) then
           begin
             if tmpSep2 = len - 3 then exit; //must be either y, yy or yyyy
             mm := strToWord(text,1,tmpSep1-1);
             dd := strToWord(text,tmpSep1+1,tmpSep2-tmpSep1-1);
             yy := strToWord(text,tmpSep2+1,len-tmpSep2);
           end else
           begin
             mm := strToWord(text,1,2);
             dd := strToWord(text,3,2);
             yy := strToWord(text,5,len-4);
           end;
    end;
    if yy + mm + dd = 0 then
    begin
      result := fZeroDateIsValid;
      dt := 0;
      exit;
    end;
    if not hasCentury then
    begin
      if yy >= fEpoch mod 100 then
        inc(yy, (fEpoch div 100)*100) else
        inc(yy, (fEpoch div 100)*100 +100);
    end;

//    if yy=0 then yy := 2016;

    result := TryEncodeDate(yy,mm,dd,dt) and DoCheckDate(dt);
  except
    result := false;
  end;
end;
//------------------------------------------------------------------------------

function TXDateEdit.IsValidDate: boolean;
var
  tmpDt: TDateTime;
begin
  Result := GetDisplayedDate(tmpDt);
end;
//------------------------------------------------------------------------------

procedure TXDateEdit.SetUseOSDateFormat(value: boolean);
begin
  if value = fUseOSDateFormat then exit;
  if value or (csDesigning in ComponentState) then
  begin
    fUseOSDateFormat := false; //otherwise SetDateFormat() wont do its stuff
    SetDateFormat(formatsettings.ShortDateFormat);
  end;
  fUseOSDateFormat := value;
end;
//------------------------------------------------------------------------------

function TXDateEdit.GetDate: TDateTime;
begin
  //nb: No error will be raised when reading the Date property if the displayed
  //date is invalid as the the date prior to editing will be returned. (Invalid
  //displayed dates are only possible while editing - ie while focused.)
  //The IsValidDate public method can be used to evaluate the 'displayed' date.
  if not GetDisplayedDate(result) then
    result := FLastValidDate;
end;
//------------------------------------------------------------------------------

procedure TXDateEdit.SetDate(NewDate: TDateTime);
begin
  if (NewDate = 0) and not fZeroDateIsValid then
    raise Exception.Create(s_invalid_date + FBlankDate)
  else if not DoCheckDate(NewDate) then
    raise Exception.Create(s_invalid_date + FormatDatetime(FDateFormat,NewDate));
  FLastValidDate := NewDate;
  ReformatText;
end;
//------------------------------------------------------------------------------

procedure TXDateEdit.CMExit(var Message: TCMExit);
var
  dudStr: string;
  tmpDt: TDateTime;
  xxd, xxm, xxy: word;
begin

  if length(text) = 4 then begin // se mete dia y mes
     DecodeDate(now, xxy, xxm, xxd);
     text:= text+inttostr(xxy);
  end;

  if (length(text) = 5) and (copy(text,3,1) = '/') then begin // se mete dia y mes
     DecodeDate(now, xxy, xxm, xxd);
     text:= text+'/'+inttostr(xxy);
  end;

  if GetDisplayedDate(tmpDt) then
  begin
    FLastValidDate := tmpDt;
    ReformatText;
    inherited;
  end
  else if assigned(FDoInvalidDate) then
    //either perform the custom error handling if it's assigned
    FDoInvalidDate(self)
  else
  begin
    //otherwise refocus the control and show the default error message
    dudStr := text;
    SetFocus;  //redisplays the last valid date via CMEnter() method
    Font.Color := FColorOK;
    raise Exception.create(format('%s"%s"',[s_invalid_date,dudStr]));
  end;
end;
//------------------------------------------------------------------------------

procedure TXDateEdit.CreateWnd;
begin
  inherited CreateWnd;
  AdjustWidth;
end;
//------------------------------------------------------------------------------

procedure TXDateEdit.CMFontChanged(var Message: TMessage);
begin
  if not fChangingColorOnly then
    AdjustWidth;
  inherited;
end;
//------------------------------------------------------------------------------

procedure TXDateEdit.AdjustWidth;
var
  DC: HDC;
  SaveFont: HFont;
  Size: TSize;
begin
  if not handleAllocated then exit;
  //adjust XDateEdit width...
  DC := GetDC(handle);
  try
    SaveFont := SelectObject(DC, Font.Handle);
    if gettextextentpoint32(DC,pchar(FBlankDate+'0'),
      length(FBlankDate)+1,Size) then
        clientwidth := Size.cx;
    SelectObject(DC, SaveFont);
  finally
    ReleaseDC(handle, DC);
  end;
end;
//------------------------------------------------------------------------------

procedure TXDateEdit.KeyPress(var Key: Char);
var
  tmpSelstart: integer;
begin
  inherited KeyPress(Key);
  if key = #13 then
  begin
    date := now;
    key := #0;
  end;
  if key = #27 then
  begin
    //[ESC] reverts date to last valid date
    ReformatText;
    SelectAll;
    key := #0;
  end;
  if key < #32 then exit; //don't block these (eg ^C, ^V, ^X)
  if (key in ['+','-']) and not (FLastValidDate = 0) and IsValidDate then
  begin
    //allow + and - to increment and decrement the date respectively ...
    if (key = '+') and DoCheckDate(FLastValidDate +1) then
      Date := FLastValidDate +1
    else if (key = '-') and DoCheckDate(FLastValidDate -1) then
      Date := FLastValidDate -1;
    Key := #0;
    SelectAll;
  end
  else if not (Key in [formatsettings.dateSeparator, '0'..'9']) then
  begin
    //reject invalid keys ...
    Key := #0;
    beep;
  end
  else if (sellength = 0) and (length(text) > 9) then
  begin
    //if the date string is too long the trash trailing chars ...
    tmpSelstart := selstart;
    inherited text := copy(text,1,selstart);
    selstart := tmpSelstart; //repositions caret for char entry
  end;
end;
//------------------------------------------------------------------------------

procedure TXDateEdit.Change;
begin
  inherited;
  if not handleallocated then exit; //see history 260503
  fChangingColorOnly := true;
  try
    if not IsValidDate then
      Font.Color := FColorInvalid else
      Font.Color := FColorOK;
  finally
    fChangingColorOnly := false;
  end;
end;
//------------------------------------------------------------------------------

function TXDateEdit.DoCheckDate(Date: TDatetime): boolean;
begin
  if assigned(FDoCheckDate) then
    Result := FDoCheckDate(self, Date) else
    Result := true;
end;
//------------------------------------------------------------------------------


end.

 


 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...