const
CLongDateFormat = 'yyyy''年''MM''月''dd''日''';
CShortDateFormat = 'yyyy/MM/dd';
CNullDateFormat = '';
CaleMinDate = -53688; // 1753/01/01 (-53798 while cause TMonthCalendar Error!!!)
CaleMaxDate = 2958465; // 9999/12/31 (2958466 while cause TMonthCalendar Error!!!)
{ TPopupCale }
constructor TPopupCale.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
if NewStyleControls then ControlStyle := [csOpaque, csDoubleClicks, csNoDesignVisible, csReplicatable]
else ControlStyle := [csOpaque, csFramed, csDoubleClicks, csNoDesignVisible, csReplicatable];
end;
procedure TPopupCale.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do
begin
Style := WS_POPUP or WS_BORDER;
ExStyle := WS_EX_TOOLWINDOW;
AddBiDiModeExStyle(ExStyle);
WindowClass.Style := CS_SAVEBITS;
end;
end;
procedure TOopsDatePicker.CloseUp(Accept: Boolean);
begin
if FListVisible then
begin
SetWindowPos(FCale.Handle, 0, 0, 0, 0, 0, SWP_NOZORDER or SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE or SWP_HIDEWINDOW);
FListVisible := False;
if Accept then begin FDateIsEmpty:=False; Date:=FCale.Date; Change; end;
Invalidate;
SetFocus;
if Assigned(FOnCloseUp) then FOnCloseUp(Self);
end;
end;
procedure TOopsDatePicker.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do if NewStyleControls and Ctl3D
then ExStyle := ExStyle or WS_EX_CLIENTEDGE
else Style := Style or WS_BORDER;
end;
procedure TOopsDatePicker.Change;
begin
if Assigned(FOnChange) then FOnChange(Self);
end;
procedure TOopsDatePicker.DropDown;
var P: TPoint;
Y: Integer;
begin
if not FListVisible then
begin
if Assigned(FOnDropDown) then FOnDropDown(Self);
FListVisible := True;
FCale.Date:=FDate;
P := Parent.ClientToScreen(Point(Left, Top));
Y := P.Y + Height;
if Y + FCale.Height > Screen.Height then Y := P.Y - FCale.Height;
if P.X<0 then P.X:=0;
if P.X+FCale.Width > Screen.Width then P.X := Screen.Width - FCale.Width;
SetWindowPos(FCale.Handle, HWND_TOP, P.X, Y, 0, 0, SWP_NOSIZE or SWP_NOACTIVATE or SWP_SHOWWINDOW);
DrawBtnPopup;
end;
end;
procedure TOopsDatePicker.KeyDown(var Key: Word; Shift: TShiftState);
var y, m, d: Word;
begin
inherited KeyDown(Key, Shift);
if (ssAlt in Shift) and ((Key=VK_UP) or (Key=VK_DOWN)) then begin if FListVisible then CloseUp(True) else DropDown; Key := 0 end;
if FListVisible then FCale.KeyDown(Key, Shift)
else
if (not FDateIsEmpty)or(Key=VK_HOME) then begin
case Key of
VK_UP : begin DecodeDate(Date, y, m, d);
if y>9998 then Exit;
case FDatePart of
0: begin
y:=y+1; if (m=2) and (d>28) and (not IsLeapYear) then d:=28;
Date:=EncodeDate(y, m, d) end;
1: Date:=IncMonth(Date,1);
2: Date:=Date+1; end;
if FDatePart=0 then FUserCharPos:=1;
if FDatePart=1 then FUserCharPos:=5;
if FDatePart=2 then FUserCharPos:=7;
Change;
end;
VK_DOWN : begin DecodeDate(Date, y, m, d);
if y<1754 then Exit;
case FDatePart of
0: begin
y:=y-1; if (m=2) and (d>28) and (not IsLeapYear) then d:=28;
Date:=EncodeDate(y, m, d) end;
1: Date:=IncMonth(Date,-1);
2: Date:=Date-1; end;
if FDatePart=0 then FUserCharPos:=1;
if FDatePart=1 then FUserCharPos:=5;
if FDatePart=2 then FUserCharPos:=7;
Change;
end;
VK_LEFT : begin
if FDatePart<1 then FDatePart:=2 else FDatePart:=FDatePart-1;
if FDatePart=0 then FUserCharPos:=1;
if FDatePart=1 then FUserCharPos:=5;
if FDatePart=2 then FUserCharPos:=7;
DrawDateText;
end;
VK_RIGHT : begin
if FDatePart>1 then FDatePart:=0 else FDatePart:=FDatePart+1;
if FDatePart=0 then FUserCharPos:=1;
if FDatePart=1 then FUserCharPos:=5;
if FDatePart=2 then FUserCharPos:=7;
DrawDateText;
end;
VK_HOME : begin
FDateIsEmpty:=False;
FDatePart:=0;
FUserCharPos:=1;
Date:=Trunc(Now);
Change;
end;
VK_DELETE: begin
SetDateToEmpty(True);
Change;
end;
VK_BACK : begin
if FUserCharPos>1 then FUserCharPos:=FUserCharPos-1;
if FUserCharPos in [1..4] then FDatePart:=0;
if FUserCharPos in [5, 6] then FDatePart:=1;
if FUserCharPos in [7, 8] then FDatePart:=2;
DrawDateText;
end;
end;
end;
end;
procedure TOopsDatePicker.KeyPress(var Key: Char);
var y, m, d: Word;
s: string;
begin
inherited KeyPress(Key);
if FListVisible then begin if Key in [#13, #27] then CloseUp(Key = #13) end
else begin
if not (Key in ['0'..'9']) then Exit;
if FDateIsEmpty then SetDateToEmpty(False);
s := FormatDateTime('yyyymmdd',Date);
s[FUserCharPos]:=Key;
y:=StrToInt(Copy(s, 1, 4));
m:=StrToInt(Copy(s, 5, 2));
d:=StrToInt(Copy(s, 7, 2));
if y<1753 then y:=1753;
if m>12 then m:=12;
if m<1 then m:=1;
if d<1 then d:=1;
if (d>31) and (m in [1,3,5,7,8,10,12]) then d:=31;
if (d>30) and (m in [4,6,9,11]) then d:=30;
if (m=2) and (d>28) then if IsLeapYear then d:=29 else d:=28;
FUserCharPos:=FUserCharPos+1;
if FUserCharPos>8 then FUserCharPos:=1;
if FUserCharPos in [1..4] then FDatePart:=0;
if FUserCharPos in [5, 6] then FDatePart:=1;
if FUserCharPos in [7, 8] then FDatePart:=2;
Date:=EncodeDate(y,m,d);
Change;
end;
end;
procedure TOopsDatePicker.DrawBtnPopup;
var Flags: Integer;
begin
if FListVisible
then Flags := DFCS_SCROLLCOMBOBOX or DFCS_FLAT or DFCS_PUSHED
else Flags := DFCS_SCROLLCOMBOBOX;
DrawFrameControl(Canvas.Handle, FRectBtnPopup, DFC_SCROLL, Flags);
end;
procedure TOopsDatePicker.DrawBtnClear;
var R: TRect;
Flags: Integer;
begin
if FPressedClear then Flags := DFCS_FLAT or DFCS_PUSHED else Flags := DFCS_SCROLLUP;
DrawFrameControl(Canvas.Handle, FRectBtnClear, DFC_SCROLL, Flags);
if FPressedClear
then R:=Bounds(FRectBtnClear.Left + ((FButtonWidth-FBitmapBtbClear.Width) div 2) + 1,
(ClientHeight-FBitmapBtbClear.Height) div 2 + 1, FBitmapBtbClear.Width, FBitmapBtbClear.Height)
else R:=Bounds(FRectBtnClear.Left + ((FButtonWidth-FBitmapBtbClear.Width) div 2),
(ClientHeight-FBitmapBtbClear.Height) div 2, FBitmapBtbClear.Width, FBitmapBtbClear.Height);
Canvas.BrushCopy(R, FBitmapBtbClear, Rect(0, 0, FBitmapBtbClear.Width, FBitmapBtbClear.Height), clOlive);
end;
procedure TOopsDatePicker.DrawDateText;
begin
Canvas.Font := Font;
Canvas.Brush.Color := Color;
if FDateIsEmpty then FDateText:=CNullDateFormat else FDateText:=FormatDateTime(FDateStr,FDate);
UpdateDispRect;
if SysLocale.MiddleEast then TControlCanvas(Canvas).UpdateTextFlags;
Canvas.TextRect(FRectDateText, 1, 2, FDateText);
if FHasFocus and not FListVisible then
begin
Canvas.Font.Color := clHighlightText;
Canvas.Brush.Color := clHighlight;
if SysLocale.MiddleEast then TControlCanvas(Canvas).UpdateTextFlags;
Canvas.TextRect(FRectYMDText[FDatePart], 1, 2, FDateText);
if FDateIsEmpty then Canvas.DrawFocusRect(FRectDateText);
end;
end;
procedure TOopsDatePicker.ListMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var Rect: TRect;
begin
Rect := FCale.ClientRect;
Rect.Top:=Rect.Top+40;
PtInRect(Rect, Point(X, Y));
if (Button = mbLeft) and PtInRect(Rect, Point(X, Y))
then CloseUp(PtInRect(FCale.ClientRect, Point(X, Y)));
end;
procedure TOopsDatePicker.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Button = mbLeft then
begin
SetFocus;
if not FHasFocus then Exit;
if FListVisible then CloseUp(False) else
begin
MouseCapture := True;
FTracking := True;
TrackButton(X, Y);
if PtInRect(FRectBtnPopup, Point(X, Y)) then DropDown;
if PtInRect(FRectYMDText[0], Point(X, Y)) then begin FDatePart:=0; FUserCharPos:=1; DrawDateText end;
if PtInRect(FRectYMDText[1], Point(X, Y)) then begin FDatePart:=1; FUserCharPos:=5; DrawDateText end;
if PtInRect(FRectYMDText[2], Point(X, Y)) then begin FDatePart:=2; FUserCharPos:=7; DrawDateText end;
end;
end;
inherited MouseDown(Button, Shift, X, Y);
end;
procedure TOopsDatePicker.MouseMove(Shift: TShiftState; X, Y: Integer);
var
ListPos: TPoint;
MousePos: TSmallPoint;
begin
if FTracking then
begin
TrackButton(X, Y);
if FListVisible then
begin
ListPos := FCale.ScreenToClient(ClientToScreen(Point(X, Y)));
if PtInRect(FCale.ClientRect, ListPos) then
begin
StopTracking;
MousePos := PointToSmallPoint(ListPos);
SendMessage(FCale.Handle, WM_LBUTTONDOWN, 0, Integer(MousePos));
Exit;
end;
end;
end;
inherited MouseMove(Shift, X, Y);
end;
procedure TOopsDatePicker.MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
if FPressedClear and (not FDateIsEmpty) then begin Change; SetDateToEmpty(True); end;
StopTracking;
inherited MouseUp(Button, Shift, X, Y);
end;
procedure TOopsDatePicker.Paint;
begin
UpdateDispRect;
DrawDateText;
DrawBtnClear;
DrawBtnPopup;
end;
function TOopsDatePicker.GetBorderSize: Integer;
var
Params: TCreateParams;
R: TRect;
begin
CreateParams(Params);
SetRect(R, 0, 0, 0, 0);
AdjustWindowRectEx(R, Params.Style, False, Params.ExStyle);
Result := R.Bottom - R.Top;
end;
function TOopsDatePicker.GetTextHeight: Integer;
var
DC: HDC;
SaveFont: HFont;
Metrics: TTextMetric;
begin
DC := GetDC(0);
SaveFont := SelectObject(DC, Font.Handle);
GetTextMetrics(DC, Metrics);
SelectObject(DC, SaveFont);
ReleaseDC(0, DC);
Result := Metrics.tmHeight;
end;
procedure TOopsDatePicker.SetDateToEmpty(Value: Boolean);
begin
if FDateIsEmpty = Value then Exit;
FUserCharPos:=1;
FDateIsEmpty := Value;
FDatePart := 0;
Invalidate;
end;
procedure TOopsDatePicker.SetDate(Value: TDate);
begin
if FDate=Value then Exit;
FDate:=Value;
FCale.Date:=Value;
Invalidate;
end;
procedure TOopsDatePicker.SetDateFormat(Value: TDTDateFormat);
begin
if FDateFormat <> Value then
begin
FDateFormat := Value;
if FDateFormat=dfLong
then FDateStr:=CLongDateFormat else FDateStr:=CShortDateFormat;
DrawDateText;
end;
end;
procedure TOopsDatePicker.StopTracking;
begin
if FTracking then
begin
TrackButton(-1, -1);
FTracking := False;
MouseCapture := False;
end;
end;
procedure TOopsDatePicker.TrackButton(X, Y: Integer);
var NewStateClear, NewStateDropDown: Boolean;
begin
NewStateClear := PtInRect(FRectBtnClear, Point(X, Y));
NewStateDropDown := PtInRect(FRectBtnPopup, Point(X, Y));
if FPressedClear <> NewStateClear then begin FPressedClear := NewStateClear; DrawBtnClear end;
if FPressedDropDown <> NewStateDropDown then begin FPressedDropDown := NewStateDropDown; DrawBtnPopup end;
end;
procedure TOopsDatePicker.CMCancelMode(var Message: TCMCancelMode);
begin
if (Message.Sender <> Self) and (Message.Sender <> FCale) then
CloseUp(False);
end;
procedure TOopsDatePicker.WMCancelMode(var Message: TMessage);
begin
StopTracking;
inherited;
end;
procedure TOopsDatePicker.WMGetDlgCode(var Message: TMessage);
begin
Message.Result := DLGC_WANTARROWS or DLGC_WANTCHARS;
end;
procedure TOopsDatePicker.CMCtl3DChanged(var Message: TMessage);
begin
if NewStyleControls then
begin
RecreateWnd;
Height := 0;
end;
inherited;
end;
procedure TOopsDatePicker.CMFontChanged(var Message: TMessage);
begin
inherited;
Height := 0;
end;
function TOopsDBDatePicker.GetDataSource: TDataSource;
begin
Result := FDataLink.DataSource;
end;
procedure TOopsDBDatePicker.SetDataSource(Value: TDataSource);
begin
if not (FDataLink.DataSourceFixed and (csLoading in ComponentState)) then
FDataLink.DataSource := Value;
if Value <> nil then Value.FreeNotification(Self);
end;
function TOopsDBDatePicker.GetDataField: string;
begin
Result := FDataLink.FieldName;
end;
procedure TOopsDBDatePicker.SetDataField(const Value: string);
begin
FDataLink.FieldName := Value;
end;
function TOopsDBDatePicker.GetReadOnly: Boolean;
begin
Result := FDataLink.ReadOnly;
end;
procedure TOopsDBDatePicker.SetReadOnly(Value: Boolean);
begin
FDataLink.ReadOnly := Value;
end;
function TOopsDBDatePicker.GetField: TField;
begin
Result := FDataLink.Field;
end;
procedure TOopsDBDatePicker.DataChange(Sender: TObject);
begin
if FDataLink.Editing then Exit;
if FDataLink.Field <> nil then
begin
if not FDataLink.Field.IsNull then Date:=FDataLink.Field.AsDateTime;
DateIsEmpty:=FDataLink.Field.IsNull;
end else
begin
DateIsEmpty:=True;
end;
end;
procedure TOopsDBDatePicker.UpdateData(Sender: TObject);
begin
if DateIsEmpty
then FDataLink.Field.Clear
else FDataLink.Field.AsDateTime := Date;
end;
procedure TOopsDBDatePicker.CMGetDataLink(var Message: TMessage);
begin
Message.Result := Integer(FDataLink);
end;
function TOopsDBDatePicker.ExecuteAction(Action: TBasicAction): Boolean;
begin
Result := inherited ExecuteAction(Action) or (FDataLink <> nil) and
FDataLink.ExecuteAction(Action);
end;
function TOopsDBDatePicker.UpdateAction(Action: TBasicAction): Boolean;
begin
Result := inherited UpdateAction(Action) or (FDataLink <> nil) and
FDataLink.UpdateAction(Action);
end;
procedure Register;
begin
RegisterComponents('OopsWare', [TOopsDatePicker]);
RegisterComponents('OopsWare', [TOopsDBDatePicker]);
end;