1418 lines
41 KiB
ObjectPascal
1418 lines
41 KiB
ObjectPascal
{-----------------------------------------------------------------------------
|
|
The contents of this file are subject to the Mozilla Public License
|
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL/MPL-1.1.html
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
|
|
the specific language governing rights and limitations under the License.
|
|
|
|
The Original Code is: JvDatePickerEdit, released on 2002-10-04.
|
|
|
|
The Initial Developer of the Original Code is Oliver Giesen [giesen att lucatec dott de]
|
|
Portions created by Oliver Giesen are Copyright (C) 2002 Lucatec GmbH.
|
|
All Rights Reserved.
|
|
|
|
Contributor(s): Peter Thörnqvist.
|
|
|
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
|
located at http://jvcl.sourceforge.net
|
|
|
|
Description:
|
|
A replacement for TDateTimePicker which is better suited for keyboard-input by
|
|
ultimately descending from TCustomMaskEdit.
|
|
|
|
Other notable features (especially in comparison to the native DATETIMEPICKER):
|
|
|
|
- The control is able to construct a suitable EditMask from a valid date format
|
|
string such as the global ShortDateFormat (the default) which should make it
|
|
adapt well to regional settings / individual requirements.
|
|
|
|
- It is possible to specify a NoDateText which will be displayed when no date
|
|
is selected. The original datetimepicker would display 1899-12-31 in such
|
|
cases. This feature could be further controlled by the AllowNoDate and
|
|
NoDateShortcut properties.
|
|
|
|
Known issues / not (yet) implemented features:
|
|
|
|
- there is no real support for DateFormats containing any literal characters
|
|
other than the defined DateSeparator, especially spaces. it /might/ work in
|
|
some cases but in the majority of cases it will not.
|
|
TODO: simply disallow such characters or implement proper handling?
|
|
|
|
- as the embedded MS-calendar does not support dates prior to 1752-09-14,
|
|
neither does this control. this is not yet handled gracefully in absolutely
|
|
all situations though.
|
|
|
|
- the Min/MaxYear contstraints are currently commented out as they are not
|
|
functional in the current state. They would still require some work to make
|
|
up for two-digit year entries.
|
|
|
|
- the control does (currently) not allow for time entry
|
|
- it really is a control for date entry only.
|
|
|
|
-----------------------------------------------------------------------------}
|
|
// $Id: JvDatePickerEdit.pas 11299 2007-05-23 16:27:35Z ahuser $
|
|
|
|
unit JvDatePickerEdit;
|
|
|
|
{$I jvcl.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF UNITVERSIONING}
|
|
JclUnitVersioning,
|
|
{$ENDIF UNITVERSIONING}
|
|
{$IFDEF COMPILER5}
|
|
Forms,
|
|
{$ENDIF COMPILER5}
|
|
Windows, Messages, Classes, Controls, ImgList,
|
|
JvCalendar, JvDropDownForm, JvCheckedMaskEdit, JvToolEdit;
|
|
|
|
type
|
|
{Types used to handle and convert between date format strings and EditMasks:}
|
|
TJvDateFigure = (dfNone, dfYear, dfMonth, dfDay);
|
|
TJvDateFigureInfo = record
|
|
Figure: TJvDateFigure;
|
|
Start: Byte;
|
|
Length: Byte;
|
|
Index: Byte;
|
|
end;
|
|
TJvDateFigures = array[ 0..2] of TJvDateFigureInfo;
|
|
|
|
{A dropdown form with an embedded calendar control.}
|
|
TJvDropCalendar = class(TJvCustomDropDownForm)
|
|
private
|
|
FCal: TJvCustomMonthCalendar;
|
|
FWithBeep: Boolean;
|
|
FOnChange: TNotifyEvent;
|
|
FOnSelect: TNotifyEvent;
|
|
FOnCancel: TNotifyEvent;
|
|
procedure CalSelChange(Sender: TObject; StartDate, EndDate: TDateTime);
|
|
procedure CalSelect(Sender: TObject; StartDate, EndDate: TDateTime);
|
|
procedure CalKeyPress(Sender: TObject; var Key: Char);
|
|
procedure CalKillFocus(const ASender: TObject; const ANextControl: TWinControl);
|
|
protected
|
|
procedure DoCancel;
|
|
procedure DoChange;
|
|
procedure DoSelect;
|
|
procedure DoShow; override;
|
|
function GetSelDate: TDateTime;
|
|
procedure SetSelDate(const AValue: TDateTime);
|
|
public
|
|
constructor CreateWithAppearance(AOwner: TComponent;
|
|
const AAppearance: TJvMonthCalAppearance);
|
|
destructor Destroy; override;
|
|
procedure SetFocus; override;
|
|
property SelDate: TDateTime read GetSelDate write SetSelDate;
|
|
property WithBeep: Boolean read FWithBeep write FWithBeep;
|
|
property OnCancel: TNotifyEvent read FOnCancel write FOnCancel;
|
|
property OnChange: TNotifyEvent read FOnChange write FOnChange;
|
|
property OnSelect: TNotifyEvent read FOnSelect write FOnSelect;
|
|
end;
|
|
|
|
TJvGetValidDateStringEvent = procedure(Sender: TObject; var DateText: string) of object;
|
|
|
|
TJvCustomDatePickerEdit = class(TJvCustomCheckedMaskEdit)
|
|
private
|
|
FAllowNoDate: Boolean;
|
|
FCalAppearance: TJvMonthCalAppearance;
|
|
FDate: TDateTime;
|
|
FDateError: Boolean;
|
|
FDeleting: Boolean;
|
|
FDateFigures: TJvDateFigures;
|
|
FInternalDateFormat,
|
|
FDateFormat: string;
|
|
FEnableValidation: Boolean;
|
|
FMask: string;
|
|
FNoDateShortcut: TShortcut;
|
|
FNoDateText: string;
|
|
FStoreDate: Boolean;
|
|
FAlwaysReturnEditDate: Boolean;
|
|
FEmptyMaskText: string;
|
|
FStoreDateFormat: Boolean;
|
|
FDateSeparator: Char;
|
|
FPopupDate: TDateTime;
|
|
FOnGetValidDateString: TJvGetValidDateStringEvent;
|
|
// FMinYear: Word;
|
|
// FMaxYear: Word;
|
|
procedure CalChange(Sender: TObject);
|
|
procedure CalDestroy(Sender: TObject);
|
|
procedure CalSelect(Sender: TObject);
|
|
procedure CalCloseQuery(Sender: TObject; var CanClose: Boolean);
|
|
function AttemptTextToDate(const AText: string; var ADate: TDateTime;
|
|
const AForce: Boolean = False; const ARaise: Boolean = False): Boolean;
|
|
function DateFormatToEditMask(var ADateFormat: string): string;
|
|
function DateToText(const ADate: TDateTime): string;
|
|
function DetermineDateSeparator(AFormat: string): Char;
|
|
procedure ResetDateFormat;
|
|
procedure FindSeparators(var AFigures: TJvDateFigures; const AText: string; const AGetLengths: Boolean = True);
|
|
procedure ParseFigures(var AFigures: TJvDateFigures; AFormat: string; const AMask: string);
|
|
procedure RaiseNoDate;
|
|
procedure SetAllowNoDate(const AValue: Boolean);
|
|
procedure SetCalAppearance(const AValue: TJvMonthCalAppearance);
|
|
function GetDate: TDateTime;
|
|
procedure SetDate(const AValue: TDateTime);
|
|
procedure SetDateFormat(const AValue: string);
|
|
function GetDropped: Boolean;
|
|
procedure SetNoDateText(const AValue: string);
|
|
procedure SetDateSeparator(const AValue: Char);
|
|
function GetEditMask: string;
|
|
procedure SetEditMask(const AValue: string);
|
|
function GetText: TCaption;
|
|
procedure SetText(const AValue: TCaption);
|
|
procedure WMPaste(var Msg: TMessage); message WM_PASTE;
|
|
procedure CMExit(var Msg: TMessage); message CM_EXIT;
|
|
protected
|
|
procedure CalChanged; virtual;
|
|
procedure RestoreMaskForKeyPress;
|
|
function GetValidDateString(const Text: string): string; virtual;
|
|
procedure AcceptValue(const Value: Variant); override;
|
|
function AcceptPopup(var Value: Variant): Boolean; override;
|
|
function IsNoDateShortcutStored: Boolean;
|
|
function IsNoDateTextStored: Boolean;
|
|
procedure PopupChange; override;
|
|
procedure Change; override;
|
|
procedure Loaded; override;
|
|
procedure CreateWnd; override;
|
|
procedure DoKillFocus(const ANextControl: TWinControl); override;
|
|
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
|
|
procedure KeyPress(var Key: Char); override;
|
|
procedure CreatePopup; override;
|
|
procedure HidePopup; override;
|
|
procedure ShowPopup(Origin: TPoint); override;
|
|
procedure DoCtl3DChanged; override;
|
|
procedure EnabledChanged; override;
|
|
function GetChecked: Boolean; override;
|
|
function GetPopupValue: Variant; override;
|
|
procedure SetChecked(const AValue: Boolean); override;
|
|
procedure SetPopupValue(const Value: Variant); override;
|
|
procedure SetShowCheckbox(const AValue: Boolean); override;
|
|
function GetEnableValidation: Boolean; virtual;
|
|
procedure UpdateDisplay; virtual;
|
|
function ValidateDate(const ADate: TDateTime): Boolean; virtual;
|
|
function ActiveFigure: TJvDateFigureInfo;
|
|
procedure ClearMask;
|
|
procedure RestoreMask;
|
|
function IsEmptyMaskText(const AText: string): Boolean;
|
|
property AllowNoDate: Boolean read FAllowNoDate write SetAllowNoDate;
|
|
property AlwaysReturnEditDate: Boolean read FAlwaysReturnEditDate write FAlwaysReturnEditDate default True;
|
|
property CalendarAppearance: TJvMonthCalAppearance read FCalAppearance write SetCalAppearance;
|
|
property Date: TDateTime read GetDate write SetDate stored FStoreDate;
|
|
property DateFormat: string read FDateFormat write SetDateFormat stored FStoreDateFormat;
|
|
property DateSeparator: Char read FDateSeparator write SetDateSeparator stored FStoreDateFormat;
|
|
property Dropped: Boolean read GetDropped;
|
|
property EnableValidation: Boolean read GetEnableValidation write FEnableValidation default True;
|
|
property ImageKind default ikDropDown;
|
|
// property MaxYear: Word read FMaxYear write FMaxYear;
|
|
// property MinYear: Word read FMinYear write FMinYear;
|
|
property NoDateShortcut: TShortcut read FNoDateShortcut write FNoDateShortcut stored IsNoDateShortcutStored;
|
|
property NoDateText: string read FNoDateText write SetNoDateText stored IsNoDateTextStored;
|
|
property ShowButton default True;
|
|
property StoreDate: Boolean read FStoreDate write FStoreDate default False;
|
|
property StoreDateFormat: Boolean read FStoreDateFormat write FStoreDateFormat default False;
|
|
|
|
property OnGetValidDateString: TJvGetValidDateStringEvent read FOnGetValidDateString write FOnGetValidDateString;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
class function DefaultImageIndex: TImageIndex; override;
|
|
procedure Clear; override;
|
|
function IsEmpty: Boolean; virtual;
|
|
|
|
function HasValidDate: Boolean;
|
|
|
|
property EditMask: string read GetEditMask write SetEditMask;
|
|
property Text: TCaption read GetText write SetText;
|
|
end;
|
|
|
|
TJvDatePickerEdit = class(TJvCustomDatePickerEdit)
|
|
public
|
|
property Dropped;
|
|
published
|
|
property Action;
|
|
property Align;
|
|
property AllowNoDate;
|
|
property AlwaysReturnEditDate;
|
|
property Anchors;
|
|
property AutoSelect;
|
|
property AutoSize;
|
|
property BorderStyle;
|
|
property ButtonFlat;
|
|
property ButtonHint;
|
|
property ButtonWidth;
|
|
property CalendarAppearance;
|
|
property Caret;
|
|
property CharCase;
|
|
property Checked;
|
|
property ClickKey;
|
|
property ClipboardCommands;
|
|
property Color;
|
|
property Constraints;
|
|
property Date;
|
|
property DateFormat;
|
|
property DateSeparator;
|
|
{$IFDEF VCL}
|
|
{property BiDiMode;}
|
|
{property ParentBiDiMode;}
|
|
{$IFDEF COMPILER6_UP}
|
|
property BevelEdges;
|
|
property BevelInner;
|
|
property BevelKind default bkNone;
|
|
property BevelOuter;
|
|
{$ENDIF COMPILER6_UP}
|
|
property Flat;
|
|
property ImeMode;
|
|
property ImeName;
|
|
property OEMConvert;
|
|
property ParentFlat;
|
|
property OnEndDock;
|
|
property OnStartDock;
|
|
{$ENDIF VCL}
|
|
property DirectInput;
|
|
property DisabledColor;
|
|
property DisabledTextColor;
|
|
property DragCursor;
|
|
property DragKind;
|
|
property DragMode;
|
|
property Enabled;
|
|
property EnableValidation;
|
|
property Font;
|
|
property Glyph;
|
|
property GroupIndex;
|
|
property HideSelection;
|
|
property HintColor;
|
|
property HotTrack;
|
|
// property MaxYear default 2900;
|
|
// property MinYear default 1900;
|
|
property ImageIndex;
|
|
property ImageKind;
|
|
property Images;
|
|
property NoDateShortcut;
|
|
property NoDateText;
|
|
property NumGlyphs;
|
|
property ParentColor;
|
|
property ParentFont;
|
|
property ParentShowHint;
|
|
property PopupMenu;
|
|
property ReadOnly;
|
|
property ShowCheckBox;
|
|
property ShowHint;
|
|
property ShowButton;
|
|
property StoreDate;
|
|
property StoreDateFormat;
|
|
property TabOrder;
|
|
property TabStop;
|
|
property Visible;
|
|
property OnButtonClick;
|
|
property OnChange;
|
|
property OnClick;
|
|
property OnCheckClick;
|
|
property OnContextPopup;
|
|
property OnDblClick;
|
|
property OnDragDrop;
|
|
property OnDragOver;
|
|
property OnEnabledChanged;
|
|
property OnEndDrag;
|
|
property OnEnter;
|
|
property OnExit;
|
|
property OnKeyDown;
|
|
property OnKeyPress;
|
|
property OnKeyUp;
|
|
property OnKillFocus;
|
|
property OnMouseDown;
|
|
property OnMouseEnter;
|
|
property OnMouseLeave;
|
|
property OnMouseMove;
|
|
property OnMouseUp;
|
|
property OnParentColorChange;
|
|
property OnSetFocus;
|
|
property OnStartDrag;
|
|
|
|
property OnGetValidDateString;
|
|
end;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
const
|
|
UnitVersioning: TUnitVersionInfo = (
|
|
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_32/run/JvDatePickerEdit.pas $';
|
|
Revision: '$Revision: 11299 $';
|
|
Date: '$Date: 2007-05-23 18:27:35 +0200 (mer., 23 mai 2007) $';
|
|
LogPath: 'JVCL\run'
|
|
);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
implementation
|
|
|
|
uses
|
|
{$IFDEF HAS_UNIT_VARIANTS}
|
|
Variants,
|
|
{$ENDIF HAS_UNIT_VARIANTS}
|
|
SysUtils, Menus,
|
|
JclStrings,
|
|
JvVcl5Utils, // StrToXxxDef
|
|
JvConsts, JvTypes, JvResources;
|
|
|
|
const
|
|
DateMaskSuffix = '!;1;_';
|
|
|
|
//=== { TJvCustomDatePickerEdit } ============================================
|
|
|
|
procedure TJvCustomDatePickerEdit.AcceptValue(const Value: Variant);
|
|
var
|
|
TextBefore: string;
|
|
TmpDate: TDateTime;
|
|
TmpValue: Variant;
|
|
OldFormat: string;
|
|
OldSeparator: Char;
|
|
begin
|
|
TextBefore := Text;
|
|
|
|
// Mantis 3056: If the date format is not the system's default, the value
|
|
// displayed in the text box after having selected a date in the popup
|
|
// will be 30.12.1899. This is because the variant will be converted to a
|
|
// string using ShortDateFormat. So we change it here, to ensure it is
|
|
// the one for the control. We also have to do the cast to a string
|
|
// ourselves because VarToStr (called in TJvCustomComboEdit) ignores the
|
|
// ShortDateFormat variable.
|
|
// And we only call the inherited method this way if the variant is a
|
|
// date, or we would risk an exception trying to convert something to a
|
|
// date when it is not.
|
|
if VarIsType(Value, varDate) then
|
|
begin
|
|
OldFormat := ShortDateFormat;
|
|
OldSeparator := SysUtils.DateSeparator;
|
|
try
|
|
ShortDateFormat := FInternalDateFormat;
|
|
SysUtils.DateSeparator := FDateSeparator;
|
|
TmpDate := Value;
|
|
TmpValue := DateToStr(TmpDate);
|
|
inherited AcceptValue(TmpValue);
|
|
finally
|
|
ShortDateFormat := OldFormat;
|
|
SysUtils.DateSeparator := OldSeparator;
|
|
end;
|
|
end
|
|
else
|
|
inherited AcceptValue(TmpValue);
|
|
|
|
// Inherited AcceptValue will change the base class Text property, thus not
|
|
// calling our SetText method. As a result, we must set the date in this case
|
|
if Text <> TextBefore then
|
|
begin
|
|
AttemptTextToDate(Text, TmpDate, False);
|
|
Self.Date := TmpDate;
|
|
end;
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.ActiveFigure: TJvDateFigureInfo;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := 2 downto 0 do
|
|
{ SelStart is 0-based, FDateFigures[I].Start is 1-based }
|
|
if SelStart + 1 >= FDateFigures[I].Start then
|
|
begin
|
|
Result := FDateFigures[I];
|
|
Exit;
|
|
end;
|
|
Result.Figure := dfNone;
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.GetValidDateString(const Text: string): string;
|
|
begin
|
|
Result := Text;
|
|
if Assigned(FOnGetValidDateString) then
|
|
FOnGetValidDateString(Self, Result);
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.AttemptTextToDate(const AText: string;
|
|
var ADate: TDateTime; const AForce: Boolean; const ARaise: Boolean): Boolean;
|
|
var
|
|
OldFormat: string;
|
|
OldSeparator: Char;
|
|
OldDate: TDateTime;
|
|
Dummy: Integer;
|
|
begin
|
|
{only attempt to convert, if at least the Mask is matched
|
|
- otherwise we'd be swamped by exceptions during input}
|
|
if AForce or Validate(AText, Dummy) then
|
|
begin
|
|
OldDate := ADate;
|
|
OldFormat := ShortDateFormat;
|
|
OldSeparator := SysUtils.DateSeparator;
|
|
try
|
|
SysUtils.DateSeparator := FDateSeparator;
|
|
ShortDateFormat := FInternalDateFormat;
|
|
try
|
|
if AllowNoDate and ((Text = NoDateText) or IsEmptyMaskText(AText)) then
|
|
ADate := 0.0
|
|
else
|
|
ADate := StrToDate(StrRemoveChars(GetValidDateString(AText), [' ']));
|
|
Result := True;
|
|
except
|
|
Result := False;
|
|
if ARaise then
|
|
raise
|
|
else
|
|
begin
|
|
if AText = '' then
|
|
ADate := Now
|
|
else
|
|
ADate := OldDate;
|
|
end;
|
|
end;
|
|
finally
|
|
SysUtils.DateSeparator := OldSeparator;
|
|
ShortDateFormat := OldFormat;
|
|
end;
|
|
end
|
|
else
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.CalChange(Sender: TObject);
|
|
begin
|
|
CalChanged;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.CalChanged;
|
|
var
|
|
NewDate: TDateTime;
|
|
begin
|
|
if (FPopup is TJvDropCalendar) then
|
|
begin
|
|
NewDate := TJvDropCalendar(FPopup).SelDate;
|
|
try
|
|
if (NewDate <> Date) and EditCanModify then
|
|
Date := NewDate;
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
{ If the EditCanModify method raises an exception the popup calendar is
|
|
destroyed in the modal message loop of the exception dialog and when
|
|
it returns we are still in the WM_LBUTTONUP handler of the now destroyed
|
|
calendar. To prevent this the following code gracefully closes the popup
|
|
calendar. }
|
|
PopupCloseUp(Self, False);
|
|
raise;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.CalCloseQuery(Sender: TObject; var CanClose: Boolean);
|
|
var
|
|
P: TPoint;
|
|
begin
|
|
{If we would let the calendar close itself while clicking the button, the
|
|
DropButtonClick method would simply reopen it again as it would find the
|
|
calendar closed.}
|
|
GetCursorPos(P);
|
|
CanClose := not PtInRect(Button.BoundsRect, Button.ScreenToClient(P));
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.CalDestroy(Sender: TObject);
|
|
begin
|
|
PopupCloseUp(Self, False);
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.CalSelect(Sender: TObject);
|
|
begin
|
|
PopupCloseUp(Self, True);
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.Change;
|
|
var
|
|
lDate: TDateTime;
|
|
lFigVal: Word;
|
|
lActFig: TJvDateFigureInfo;
|
|
|
|
procedure SetActiveFigVal(const AValue: Word);
|
|
begin
|
|
BeginInternalChange;
|
|
try
|
|
SelStart := lActFig.Start - 1;
|
|
SelLength := lActFig.Length;
|
|
SelText := Format('%.*d', [lActFig.Length, AValue]);
|
|
finally
|
|
EndInternalChange;
|
|
end;
|
|
end;
|
|
|
|
procedure EnforceRange(const AMin, AMax: Word);
|
|
begin
|
|
if lFigVal > AMax then
|
|
SetActiveFigVal(AMax)
|
|
else
|
|
if lFigVal < AMin then
|
|
SetActiveFigVal(AMin);
|
|
end;
|
|
|
|
begin
|
|
if InternalChanging then
|
|
Exit;
|
|
|
|
FDateError := False;
|
|
|
|
if [csDesigning, csDestroying] * ComponentState <> [] then
|
|
Exit;
|
|
|
|
if (Text <> NoDateText) and (Text <> '') then
|
|
begin
|
|
lDate := Self.Date;
|
|
if AttemptTextToDate(Text, lDate) then
|
|
begin
|
|
BeginInternalChange;
|
|
try
|
|
Self.Date := lDate;
|
|
finally
|
|
EndInternalChange;
|
|
end;
|
|
end
|
|
else
|
|
if (not FDeleting) and EnableValidation then
|
|
begin
|
|
lActFig := ActiveFigure;
|
|
|
|
if lActFig.Figure <> dfNone then
|
|
begin
|
|
lFigVal := StrToIntDef(Trim(Copy(Text, lActFig.Start, lActFig.Length)), 0);
|
|
//only enforce range if the cursor is at the end of the current figure:
|
|
if SelStart = lActFig.Start + lActFig.Length - 1 then
|
|
case lActFig.Figure of
|
|
dfDay:
|
|
EnforceRange(1, 31);
|
|
dfMonth:
|
|
EnforceRange(1, 12);
|
|
dfYear:
|
|
{EnforceRange( MinYear, MaxYear)}; //year-validation still under development
|
|
end;
|
|
end;
|
|
{make sure querying the date in an OnChange event handler always reflects
|
|
the current contents of the control and not just the last valid value.}
|
|
lDate := 0;
|
|
AttemptTextToDate(Text, lDate, lActFig.Index = High(TJvDateFigures));
|
|
if AlwaysReturnEditDate then
|
|
FDate := lDate;
|
|
end;
|
|
end;
|
|
inherited Change;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.Clear;
|
|
begin
|
|
Checked := False;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.ClearMask;
|
|
begin
|
|
if EditMask <> '' then
|
|
begin
|
|
FMask := EditMask;
|
|
if not (csDesigning in ComponentState) then
|
|
EditMask := '';
|
|
end;
|
|
end;
|
|
|
|
constructor TJvCustomDatePickerEdit.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
|
|
FAllowNoDate := True;
|
|
FAlwaysReturnEditDate := True;
|
|
FDate := SysUtils.Date;
|
|
FDateError := False;
|
|
FDeleting := False;
|
|
FEnableValidation := True;
|
|
// FMaxYear := 2900;
|
|
// FMinYear := 1800;
|
|
FNoDateShortcut := TextToShortCut(RsDefaultNoDateShortcut);
|
|
FNoDateText := '';
|
|
FStoreDate := False;
|
|
FStoreDateFormat := False;
|
|
|
|
FCalAppearance := TJvMonthCalAppearance.Create;
|
|
|
|
ControlState := ControlState + [csCreating];
|
|
try
|
|
ImageKind := ikDropDown; { force update }
|
|
ShowButton := True;
|
|
finally
|
|
ControlState := ControlState - [csCreating];
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.CreatePopup;
|
|
begin
|
|
if not Assigned(FPopup) then
|
|
begin
|
|
FPopup := TJvDropCalendar.CreateWithAppearance(Self, FCalAppearance);
|
|
with TJvDropCalendar(FPopup) do
|
|
begin
|
|
// SelDate := Self.Date;
|
|
OnChange := Self.CalChange;
|
|
OnSelect := Self.CalSelect;
|
|
OnDestroy := Self.CalDestroy;
|
|
OnCloseQuery := Self.CalCloseQuery;
|
|
// OnKillFocus := Self.CalKillFocus;
|
|
// Show;
|
|
// SetFocus;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.CreateWnd;
|
|
begin
|
|
inherited CreateWnd;
|
|
|
|
// obones: changed to DateFormat instead of ShortDateFormat, it was
|
|
// preventing any date format different from the system's value to be
|
|
// set at design time
|
|
SetDateFormat(DateFormat);
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.DateFormatToEditMask(
|
|
var ADateFormat: string): string;
|
|
begin
|
|
StrReplace(ADateFormat, 'dddddd', LongDateFormat, []);
|
|
StrReplace(ADateFormat, 'ddddd', ShortDateFormat, []);
|
|
StrReplace(ADateFormat, 'dddd', '', []); // unsupported: DoW as full name
|
|
StrReplace(ADateFormat, 'ddd', '', []); // unsupported: DoW as abbrev
|
|
StrReplace(ADateFormat, 'MMMM', 'MM', []);
|
|
StrReplace(ADateFormat, 'MMM', 'M', []);
|
|
Result := ADateFormat;
|
|
StrReplace(Result, 'dd', '00', []);
|
|
StrReplace(Result, 'd', '99', []);
|
|
StrReplace(Result, 'MM', '00', []);
|
|
StrReplace(Result, 'M', '99', []);
|
|
StrReplace(Result, 'yyyy', '0099', []);
|
|
StrReplace(Result, 'yy', '00', []);
|
|
StrReplace(Result, ' ', '_', []);
|
|
Result := Trim(Result) + DateMaskSuffix;
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.DateToText(const ADate: TDateTime): string;
|
|
var
|
|
OldSep: Char;
|
|
begin
|
|
OldSep := SysUtils.DateSeparator;
|
|
// without this a slash would always be converted to SysUtils.DateSeparator
|
|
SysUtils.DateSeparator := Self.DateSeparator;
|
|
try
|
|
Result := FormatDateTime(FInternalDateFormat, ADate);
|
|
finally
|
|
SysUtils.DateSeparator := OldSep;
|
|
end;
|
|
end;
|
|
|
|
class function TJvCustomDatePickerEdit.DefaultImageIndex: TImageIndex;
|
|
begin
|
|
Result := TJvDateEdit.DefaultImageIndex;
|
|
end;
|
|
|
|
destructor TJvCustomDatePickerEdit.Destroy;
|
|
begin
|
|
FreeAndNil(FCalAppearance);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.DetermineDateSeparator(AFormat: string): Char;
|
|
begin
|
|
AFormat := StrRemoveChars(Trim(AFormat), ['d', 'M', 'y']);
|
|
if Length(AFormat) > 0 then
|
|
Result := AFormat[1]
|
|
else
|
|
Result := SysUtils.DateSeparator;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.DoCtl3DChanged;
|
|
begin
|
|
inherited DoCtl3DChanged;
|
|
{ (rb) Conflicts with ButtonFlat property }
|
|
Button.Flat := not Self.Ctl3D;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.CMExit(var Msg: TMessage);
|
|
var
|
|
lDate: TDateTime;
|
|
OrgEditText: string;
|
|
begin
|
|
if EnableValidation then
|
|
try
|
|
lDate := Self.Date;
|
|
if (Text <> NoDateText) and AttemptTextToDate(Text, lDate, True, True) then
|
|
Self.Date := lDate;
|
|
except
|
|
on EConvertError do
|
|
if not (csDestroying in ComponentState) then
|
|
begin
|
|
FDateError := True;
|
|
SetFocus;
|
|
raise;
|
|
end
|
|
else
|
|
Self.Date := 0;
|
|
end;
|
|
if AllowNoDate and (lDate = 0.0) then
|
|
begin
|
|
OrgEditText := EditText;
|
|
EditText := '';
|
|
end;
|
|
inherited;
|
|
if OrgEditText <> '' then
|
|
EditText := OrgEditText;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.DoKillFocus(const ANextControl: TWinControl);
|
|
begin
|
|
if ((ANextControl = nil) or ((ANextControl <> FPopup) and
|
|
(ANextControl.Owner <> FPopup))) and not FDateError then
|
|
PopupCloseUp(Self, False);
|
|
inherited DoKillFocus(ANextControl);
|
|
end;
|
|
|
|
//procedure TJvCustomDatePickerEdit.DropButtonClick(Sender: TObject);
|
|
//begin
|
|
// if Dropped then
|
|
// CloseUp
|
|
// else
|
|
// DropDown;
|
|
//end;
|
|
|
|
//procedure TJvCustomDatePickerEdit.DropDown;
|
|
//begin
|
|
// if not Dropped then
|
|
// begin
|
|
// if IsEmpty then
|
|
// Self.Date := SysUtils.Date;
|
|
//
|
|
// FDropFo := TJvDropCalendar.CreateWithAppearance(Self, FCalAppearance);
|
|
// with FDropFo do
|
|
// begin
|
|
// SelDate := Self.Date;
|
|
// OnChange := Self.CalChange;
|
|
// OnSelect := Self.CalSelect;
|
|
// OnDestroy := Self.CalDestroy;
|
|
// OnCloseQuery := Self.CalCloseQuery;
|
|
// OnKillFocus := Self.CalKillFocus;
|
|
// Show;
|
|
// SetFocus;
|
|
// end;
|
|
// end;
|
|
//end;
|
|
|
|
procedure TJvCustomDatePickerEdit.EnabledChanged;
|
|
begin
|
|
inherited EnabledChanged;
|
|
if not Enabled and Dropped then
|
|
PopupCloseUp(Self, False);
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.FindSeparators(var AFigures: TJvDateFigures;
|
|
const AText: string; const AGetLengths: Boolean);
|
|
begin
|
|
//TODO 3 : make up for escaped characters in EditMask
|
|
AFigures[0].Start := 1;
|
|
AFigures[1].Start := Pos(DateSeparator, AText) + 1;
|
|
AFigures[2].Start := StrLastPos(DateSeparator, AText) + 1;
|
|
|
|
if AGetLengths then
|
|
begin
|
|
AFigures[0].Length := AFigures[1].Start - 2;
|
|
AFigures[1].Length := AFigures[2].Start - AFigures[1].Start - 1;
|
|
AFigures[2].Length := Length(AText) - AFigures[2].Start + 1;
|
|
end;
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.GetChecked: Boolean;
|
|
begin
|
|
Result := not IsEmpty;
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.GetDate: TDateTime;
|
|
begin
|
|
Result := FDate;
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.GetDropped: Boolean;
|
|
begin
|
|
//Result := Assigned(FDropFo) and not (csDestroying in FDropFo.ComponentState);
|
|
Result := PopupVisible;
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.GetEditMask: string;
|
|
begin
|
|
Result := inherited EditMask;
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.GetEnableValidation: Boolean;
|
|
begin
|
|
Result := FEnableValidation;
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.GetPopupValue: Variant;
|
|
begin
|
|
if FPopup is TJvDropCalendar then
|
|
Result := TJvDropCalendar(FPopup).SelDate;
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.GetText: TCaption;
|
|
var
|
|
OldSep: Char;
|
|
begin
|
|
OldSep := SysUtils.DateSeparator;
|
|
SysUtils.DateSeparator := Self.DateSeparator;
|
|
try
|
|
Result := inherited Text;
|
|
finally
|
|
SysUtils.DateSeparator := OldSep;
|
|
end;
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.HasValidDate: Boolean;
|
|
var
|
|
TmpDate: TDateTime;
|
|
begin
|
|
Result := AttemptTextToDate(Text, TmpDate, False, False);
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.HidePopup;
|
|
begin
|
|
// inherited;
|
|
if FPopup is TJvDropCalendar then
|
|
begin
|
|
TJvDropCalendar(FPopup).Hide;
|
|
if Assigned(OnPopupHidden) then
|
|
OnPopupHidden(Self);
|
|
end;
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.IsEmpty: Boolean;
|
|
begin
|
|
Result := (FDate = 0);
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.IsEmptyMaskText(const AText: string): Boolean;
|
|
begin
|
|
Result := AnsiSameStr(AText, FEmptyMaskText);
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.IsNoDateShortcutStored: Boolean;
|
|
begin
|
|
Result := (NoDateShortcut <> TextToShortCut(RsDefaultNoDateShortcut));
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.IsNoDateTextStored: Boolean;
|
|
begin
|
|
Result := (NoDateText <> '');
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.RestoreMaskForKeyPress;
|
|
begin
|
|
try
|
|
if ((EditMask = '') or (EditMask <> FMask)) and (Text = NoDateText) and EditCanModify then
|
|
begin
|
|
Text := '';
|
|
RestoreMask;
|
|
end;
|
|
except
|
|
Text := '';
|
|
RestoreMask;
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.KeyDown(var Key: Word; Shift: TShiftState);
|
|
var
|
|
// Indicates whether FDeleting is set here from False to True.
|
|
DeleteSetHere: Boolean;
|
|
begin
|
|
DeleteSetHere := False;
|
|
RestoreMaskForKeyPress;
|
|
|
|
if AllowNoDate and (ShortCut(Key, Shift) = NoDateShortcut) and EditCanModify then
|
|
Date := 0
|
|
else
|
|
if Shift * KeyboardShiftStates = [] then
|
|
case Key of
|
|
// VK_ESCAPE:
|
|
// begin
|
|
// CloseUp;
|
|
// Reset;
|
|
// end;
|
|
// VK_DOWN:
|
|
// if AShift = [ssAlt] then
|
|
// DropDown;
|
|
VK_BACK, VK_CLEAR, VK_DELETE, VK_EREOF, VK_OEM_CLEAR:
|
|
begin
|
|
DeleteSetHere := not FDeleting;
|
|
FDeleting := True;
|
|
end;
|
|
end;
|
|
inherited KeyDown(Key, Shift);
|
|
FDeleting := FDeleting and not DeleteSetHere;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.KeyPress(var Key: Char);
|
|
var
|
|
OldSep: Char;
|
|
begin
|
|
{ If used in JvDBGrid the KeyDown event isn't invoked, so the EditMask isn't set
|
|
when the KeyPress event triggers. }
|
|
RestoreMaskForKeyPress;
|
|
|
|
{ this makes the transition easier for users used to non-mask-aware edit controls
|
|
as they could continue typing the separator character without the cursor
|
|
auto-advancing to the next figure when they don't expect it : }
|
|
if ((Key = Self.DateSeparator) and (Text[SelStart] = Self.DateSeparator)) or
|
|
((CharIsPrintable(Key) or (Key = #8)) and not EditCanModify) then
|
|
begin
|
|
Key := #0;
|
|
Exit;
|
|
end;
|
|
|
|
OldSep := SysUtils.DateSeparator;
|
|
SysUtils.DateSeparator := Self.DateSeparator;
|
|
try
|
|
inherited KeyPress(Key);
|
|
finally
|
|
SysUtils.DateSeparator := OldSep;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.Loaded;
|
|
var
|
|
SavedWidth : Integer;
|
|
begin
|
|
// (obones) Mantis 2491: After a copy and paste operation in the IDE, the new
|
|
// control would be one pixel less in width. This is caused by a call to
|
|
// SetText that triggers a call to TCustomMaskEdit.CheckCursor that sends
|
|
// WM_LEFT to the control. Somehow, this ends up being eaten by the designer
|
|
// and reduces the width. Add a call to CheckCursor just before UpdateDisplay
|
|
// below, you'll see it's reduced by two. What's weird is that if you do the
|
|
// exact same thing in Loaded in TJvCustomCheckedMaskedEdit, the width does
|
|
// not get reduced. So it must be something in this class, but I cannot
|
|
// figure out exactly what is done here to trigger this. For now, Let's just
|
|
// save and restore the width.
|
|
SavedWidth := Width;
|
|
try
|
|
inherited Loaded;
|
|
UpdateDisplay;
|
|
finally
|
|
Width := SavedWidth;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.ParseFigures(var AFigures: TJvDateFigures;
|
|
AFormat: string; const AMask: string);
|
|
var
|
|
I: Integer;
|
|
DummyFigures: TJvDateFigures;
|
|
begin
|
|
{Determine the position of the individual figures in the mask string.}
|
|
FindSeparators(AFigures, AMask);
|
|
AFigures[2].Length := AFigures[2].Length - Length(DateMaskSuffix);
|
|
|
|
AFormat := UpperCase(AFormat);
|
|
|
|
{Determine the order of the individual figures in the format string.}
|
|
FindSeparators(DummyFigures, AFormat, False);
|
|
|
|
for I := 0 to 2 do
|
|
begin
|
|
case AFormat[DummyFigures[I].Start] of
|
|
'D':
|
|
AFigures[I].Figure := dfDay;
|
|
'M':
|
|
AFigures[I].Figure := dfMonth;
|
|
'Y':
|
|
AFigures[I].Figure := dfYear;
|
|
end;
|
|
AFigures[I].Index := I;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.PopupChange;
|
|
begin
|
|
inherited PopupChange;
|
|
DoChange;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.RaiseNoDate;
|
|
begin
|
|
raise EJVCLException.CreateResFmt(@RsEMustHaveADate, [Name]);
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.ResetDateFormat;
|
|
begin
|
|
FInternalDateFormat := FDateFormat;
|
|
FMask := DateFormatToEditMask(FInternalDateFormat);
|
|
ParseFigures(FDateFigures, FInternalDateFormat, FMask);
|
|
BeginInternalChange;
|
|
try
|
|
EditMask := '';
|
|
Text := '';
|
|
EditMask := FMask;
|
|
FEmptyMaskText := Text;
|
|
finally
|
|
EndInternalChange;
|
|
end;
|
|
UpdateDisplay;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.RestoreMask;
|
|
begin
|
|
if EditMask = '' then
|
|
EditMask := FMask;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.SetAllowNoDate(const AValue: Boolean);
|
|
begin
|
|
if AllowNoDate <> AValue then
|
|
begin
|
|
FAllowNoDate := AValue;
|
|
|
|
if AValue and IsEmpty then
|
|
if csDesigning in ComponentState then
|
|
Self.Date := SysUtils.Date
|
|
else
|
|
RaiseNoDate;
|
|
|
|
if not AValue then
|
|
ShowCheckBox := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.SetCalAppearance(const AValue: TJvMonthCalAppearance);
|
|
begin
|
|
FCalAppearance.Assign(AValue);
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.SetChecked(const AValue: Boolean);
|
|
begin
|
|
inherited SetChecked(AValue);
|
|
if Checked <> AValue then
|
|
begin
|
|
if AValue then
|
|
begin
|
|
if Self.Date = 0 then
|
|
Self.Date := SysUtils.Date;
|
|
end
|
|
else
|
|
Self.Date := 0;
|
|
Change;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.SetDate(const AValue: TDateTime);
|
|
begin
|
|
if (FDate <> AValue) and ValidateDate(AValue) then
|
|
begin
|
|
StoreDate := Trunc(AValue) = Trunc(FDate);
|
|
FDate := AValue;
|
|
if AValue <> 0 then
|
|
Checked := True;
|
|
DoChange;
|
|
end;
|
|
UpdateDisplay;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.SetDateFormat(const AValue: string);
|
|
begin
|
|
FDateFormat := AValue;
|
|
if FDateFormat = '' then
|
|
FDateFormat := ShortDateFormat;
|
|
DateSeparator := DetermineDateSeparator(FDateFormat); //calls ResetDateFormat implicitly
|
|
StoreDateFormat := FDateFormat <> ShortDateFormat;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.SetDateSeparator(const AValue: Char);
|
|
begin
|
|
FDateSeparator := AValue;
|
|
ResetDateFormat;
|
|
end;
|
|
|
|
{ The only purpose of the following overrides is to overcome a known issue in
|
|
Mask.pas where it is impossible to use the slash character in an EditMask if
|
|
SysUtils.DateSeparator is set to something else even if the slash was escaped
|
|
as a literal. By inheritance the following methods all end up eventually in
|
|
Mask.MaskIntlLiteralToChar which performs the unwanted conversion. By
|
|
temporarily setting SysUtils.DateSeparator we could circumvent this. }
|
|
|
|
procedure TJvCustomDatePickerEdit.SetEditMask(const AValue: string);
|
|
var
|
|
OldSep: Char;
|
|
begin
|
|
{ if csDesigning in ComponentState then
|
|
Exit;}
|
|
|
|
OldSep := SysUtils.DateSeparator;
|
|
SysUtils.DateSeparator := Self.DateSeparator;
|
|
try
|
|
inherited EditMask := AValue;
|
|
finally
|
|
SysUtils.DateSeparator := OldSep;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.SetNoDateText(const AValue: string);
|
|
begin
|
|
FNoDateText := AValue;
|
|
UpdateDisplay;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.SetPopupValue(const Value: Variant);
|
|
var
|
|
NewDate: TDateTime;
|
|
begin
|
|
if FPopup is TJvDropCalendar then
|
|
begin
|
|
// We must do the conversion ourselves as the date format might
|
|
// have been personalized. (Mantis 3628)
|
|
// Default to Now if the Value is not valid. (Mantis 3733)
|
|
if (Value = Null) or (Value = NoDateText) or not AttemptTextToDate(VarToStr(Value), NewDate) then
|
|
NewDate := Now;
|
|
FPopupDate := NewDate;
|
|
TJvDropCalendar(FPopup).SelDate := NewDate;
|
|
end;
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.AcceptPopup(var Value: Variant): Boolean;
|
|
begin
|
|
Result := inherited AcceptPopup(Value);
|
|
if Result then
|
|
Result := Value <> FPopupDate;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.SetShowCheckbox(const AValue: Boolean);
|
|
begin
|
|
inherited SetShowCheckbox(AValue);
|
|
if AValue then
|
|
AllowNoDate := True;
|
|
UpdateDisplay;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.SetText(const AValue: TCaption);
|
|
var
|
|
OldSep: Char;
|
|
begin
|
|
OldSep := SysUtils.DateSeparator;
|
|
SysUtils.DateSeparator := Self.DateSeparator;
|
|
try
|
|
inherited Text := AValue;
|
|
finally
|
|
SysUtils.DateSeparator := OldSep;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.ShowPopup(Origin: TPoint);
|
|
begin
|
|
if FPopup is TJvDropCalendar then
|
|
TJvDropCalendar(FPopup).Show;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.UpdateDisplay;
|
|
begin
|
|
if InternalChanging or (csLoading in ComponentState) then
|
|
Exit;
|
|
|
|
// (obones): We need a valid handle here, because setting the text
|
|
// will read the value of DateSeparator. This value is #0 until the
|
|
// CreateWnd method is called.
|
|
// If we don't do that, setting any property that changes the display
|
|
// (like checked) just after having created the control at runtime
|
|
// would trigger an "Invalid date" exception because the date, month
|
|
// and day would not be separated at all.
|
|
// Doing this means that a parent is required for the change to work.
|
|
HandleNeeded;
|
|
|
|
BeginInternalChange;
|
|
try
|
|
inherited SetChecked(not IsEmpty);
|
|
if IsEmpty then
|
|
begin
|
|
if not (csDesigning in ComponentState) then
|
|
begin
|
|
ClearMask;
|
|
Text := NoDateText;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
RestoreMask;
|
|
Text := DateToText(Self.Date)
|
|
end;
|
|
finally
|
|
EndInternalChange;
|
|
end;
|
|
end;
|
|
|
|
function TJvCustomDatePickerEdit.ValidateDate(const ADate: TDateTime): Boolean;
|
|
begin
|
|
if (not AllowNoDate) and (ADate = 0) then
|
|
RaiseNoDate;
|
|
if (ADate < EncodeDate(1752, 09, 14)) or ((ADate > EncodeDate(1752, 09, 19)) and (ADate < EncodeDate(1752, 10, 1))) then
|
|
{ For historical/political reasons the days 1752-09-03 - 1752-09-13 do not
|
|
exist in the Gregorian calendar - for some unknown reason the Microsoft
|
|
calendar treats the period between 1752-09-20 and 1752-09-30 as missing
|
|
instead, even though dates before 1752-09-14 are considered invalid as
|
|
well (MS' offical explanation saying they only support the Gregorian
|
|
calendar as of British adoption of it is not accurate: Britain adopted the
|
|
Gregorian calendar starting 1752-01-01).}
|
|
Result := False
|
|
else
|
|
Result := True;
|
|
end;
|
|
|
|
procedure TJvCustomDatePickerEdit.WMPaste(var Msg: TMessage);
|
|
var
|
|
OldSep: Char;
|
|
begin
|
|
if csDesigning in ComponentState then
|
|
Exit;
|
|
|
|
OldSep := SysUtils.DateSeparator;
|
|
SysUtils.DateSeparator := Self.DateSeparator;
|
|
try
|
|
inherited;
|
|
finally
|
|
SysUtils.DateSeparator := OldSep;
|
|
end;
|
|
end;
|
|
|
|
//=== { TJvDropCalendar } ====================================================
|
|
|
|
procedure TJvDropCalendar.CalKeyPress(Sender: TObject; var Key: Char);
|
|
begin
|
|
if WithBeep then
|
|
SysUtils.Beep;
|
|
case Word(Key) of
|
|
VK_RETURN:
|
|
DoSelect;
|
|
VK_ESCAPE:
|
|
DoCancel;
|
|
else
|
|
DoChange;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvDropCalendar.CalKillFocus(const ASender: TObject;
|
|
const ANextControl: TWinControl);
|
|
var
|
|
P: TPoint;
|
|
begin
|
|
GetCursorPos(P);
|
|
if PtInRect(BoundsRect, P) then
|
|
Exit;
|
|
if Assigned(ANextControl) then
|
|
FocusKilled(ANextControl.Handle)
|
|
else
|
|
FocusKilled(0);
|
|
end;
|
|
|
|
procedure TJvDropCalendar.CalSelChange(Sender: TObject;
|
|
StartDate, EndDate: TDateTime);
|
|
begin
|
|
DoChange;
|
|
end;
|
|
|
|
procedure TJvDropCalendar.CalSelect(Sender: TObject;
|
|
StartDate, EndDate: TDateTime);
|
|
begin
|
|
DoSelect;
|
|
end;
|
|
|
|
constructor TJvDropCalendar.CreateWithAppearance(AOwner: TComponent;
|
|
const AAppearance: TJvMonthCalAppearance);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FWithBeep := False;
|
|
FCal := TJvMonthCalendar2.CreateWithAppearance(Self, AAppearance);
|
|
with TJvMonthCalendar2(FCal) do
|
|
begin
|
|
Parent := Self;
|
|
ParentFont := True;
|
|
OnSelChange := CalSelChange;
|
|
OnSelect := CalSelect;
|
|
OnKillFocus := CalKillFocus;
|
|
OnKeyPress := CalKeyPress;
|
|
Visible := True;
|
|
AutoSize := True;
|
|
end;
|
|
end;
|
|
|
|
destructor TJvDropCalendar.Destroy;
|
|
begin
|
|
if Assigned(FCal) then
|
|
with TJvMonthCalendar2(FCal) do
|
|
begin
|
|
OnSelChange := nil;
|
|
OnSelect := nil;
|
|
OnKeyPress := nil;
|
|
end;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TJvDropCalendar.DoCancel;
|
|
begin
|
|
if Assigned(OnCancel) then
|
|
OnCancel(Self)
|
|
else
|
|
Release;
|
|
end;
|
|
|
|
procedure TJvDropCalendar.DoChange;
|
|
begin
|
|
if Assigned(OnChange) then
|
|
OnChange(Self);
|
|
end;
|
|
|
|
procedure TJvDropCalendar.DoSelect;
|
|
begin
|
|
if Assigned(OnSelect) then
|
|
OnSelect(Self);
|
|
end;
|
|
|
|
procedure TJvDropCalendar.DoShow;
|
|
begin
|
|
{
|
|
In the constructor the calendar will sometimes report
|
|
the wrong size, so we do this here.
|
|
}
|
|
AutoSize := True;
|
|
TJvMonthCalendar2(FCal).Today := Date; { update the current day }
|
|
inherited DoShow;
|
|
end;
|
|
|
|
function TJvDropCalendar.GetSelDate: TDateTime;
|
|
begin
|
|
Result := TJvMonthCalendar2(FCal).DateFirst;
|
|
end;
|
|
|
|
procedure TJvDropCalendar.SetFocus;
|
|
begin
|
|
if FCal.CanFocus then
|
|
FCal.SetFocus
|
|
else
|
|
inherited SetFocus;
|
|
end;
|
|
|
|
procedure TJvDropCalendar.SetSelDate(const AValue: TDateTime);
|
|
begin
|
|
TJvMonthCalendar2(FCal).DateFirst := AValue;
|
|
end;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
initialization
|
|
RegisterUnitVersion(HInstance, UnitVersioning);
|
|
|
|
finalization
|
|
UnregisterUnitVersion(HInstance);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
end.
|
|
|