Files
lazarus-ccr/components/jujiboutils/src/jdblabeleddateedit.pas
2013-11-27 09:04:13 +00:00

499 lines
12 KiB
ObjectPascal

{ jdblabeleddateedit
Copyright (C) 2011 Julio Jiménez Borreguero
Contact: jujibo at gmail dot com
This library is free software; you can redistribute it and/or modify it
under the same terms as the Lazarus Component Library (LCL)
See the file license-jujiboutils.txt and COPYING.LGPL, included in this distribution,
for details about the license.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
}
unit jdblabeleddateedit;
{$mode objfpc}{$H+}
interface
uses
Classes, LResources, Controls, ExtCtrls, DB, DBCtrls, LMessages, LCLType, Dialogs,
SysUtils, jinputconsts, CalendarPopup, Calendar, Buttons;
type
{ TJDBLabeledDateEdit }
TJDBLabeledDateEdit = class(TCustomLabeledEdit)
private
fFormat: string;
FDataLink: TFieldDataLink;
FButton: TSpeedButton;
FButtonNeedsFocus: boolean;
function GetButtonWidth: integer;
procedure SetButtonWidth(AValue: integer);
procedure WMSetFocus(var Message: TLMSetFocus); message LM_SETFOCUS;
procedure WMKillFocus(var Message: TLMKillFocus); message LM_KILLFOCUS;
procedure DataChange(Sender: TObject);
procedure UpdateData(Sender: TObject);
procedure FocusRequest(Sender: TObject);
function GetDataField: string;
function GetDataSource: TDataSource;
function GetField: TField;
function IsReadOnly: boolean;
function getFormat: string;
procedure setFormat(const AValue: string);
procedure formatInput;
procedure SetDataField(const Value: string);
procedure SetDataSource(Value: TDataSource);
procedure CMGetDataLink(var Message: TLMessage); message CM_GETDATALINK;
function IsValidCurrency(const Value: string): boolean;
protected
procedure Loaded; override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure ActiveChange(Sender: TObject); virtual;
procedure KeyDown(var Key: word; Shift: TShiftState); override;
procedure KeyPress(var Key: char); override;
procedure DoEnter; override;
function GetReadOnly: boolean; override;
procedure SetReadOnly(Value: boolean); override;
procedure SetParent(AParent: TWinControl); override;
procedure DoPositionButton; virtual;
procedure CheckButtonVisible;
procedure CMVisibleChanged(var Msg: TLMessage); message CM_VISIBLECHANGED;
procedure CMEnabledChanged(var Msg: TLMessage); message CM_ENABLEDCHANGED;
procedure CMBiDiModeChanged(var Message: TLMessage); message CM_BIDIMODECHANGED;
procedure ShowCalendar(Sender: TObject);
procedure CalendarPopupReturnDate(Sender: TObject; const ADate: TDateTime);
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
procedure EditingDone; override;
property Field: TField read GetField;
published
property DisplayFormat: string read getFormat write setFormat;
property DataField: string read GetDataField write SetDataField;
property DataSource: TDataSource read GetDataSource write SetDataSource;
property ReadOnly: boolean read GetReadOnly write SetReadOnly default False;
property Button: TSpeedButton read FButton;
property ButtonWidth: integer read GetButtonWidth write SetButtonWidth;
property Action;
property Align;
property Alignment;
property Anchors;
property AutoSelect;
property AutoSize;
property BidiMode;
property BorderSpacing;
property BorderStyle;
property CharCase;
property Color;
property Constraints;
property DragCursor;
property DragMode;
property EditLabel;
property Enabled;
property Font;
property LabelPosition;
property LabelSpacing;
property MaxLength;
property ParentColor;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property TabOrder;
property TabStop;
property Visible;
property OnChange;
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 OnStartDrag;
property OnUTF8KeyPress;
end;
procedure Register;
implementation
uses
jcontrolutils, dateutils, jdbutils;
procedure Register;
begin
{$I ldatedbicon.lrs}
RegisterComponents('JujiboDB', [TJDBLabeledDateEdit]);
end;
{ TJDBLabeledDateEdit }
function TJDBLabeledDateEdit.GetButtonWidth: integer;
begin
Result := FButton.Width;
end;
procedure TJDBLabeledDateEdit.SetButtonWidth(AValue: integer);
begin
FButton.Width := AValue;
end;
procedure TJDBLabeledDateEdit.WMSetFocus(var Message: TLMSetFocus);
begin
CheckButtonVisible;
inherited;
end;
procedure TJDBLabeledDateEdit.WMKillFocus(var Message: TLMKillFocus);
begin
CheckButtonVisible;
inherited;
end;
procedure TJDBLabeledDateEdit.DataChange(Sender: TObject);
begin
if FDataLink.Field <> nil then
begin
if not Focused then
formatInput
else
Caption := FDataLink.Field.AsString;
end
else
Text := '';
end;
procedure TJDBLabeledDateEdit.UpdateData(Sender: TObject);
var
theValue: string;
begin
if FDataLink.Field <> nil then
begin
theValue := NormalizeDate(Text, FDataLink.Field.AsDateTime);
if Text = '' then
FDataLink.Field.Text := Text
else
if IsValidDateString(theValue) then
begin
FDataLink.Field.Text := theValue;
end
else
begin
ShowMessage(Format(SInvalidDate, [Caption]));
Caption := FDataLink.Field.AsString;
SelectAll;
SetFocus;
end;
end
else
Text := '';
end;
procedure TJDBLabeledDateEdit.FocusRequest(Sender: TObject);
begin
SetFocus;
end;
function TJDBLabeledDateEdit.GetDataField: string;
begin
Result := FDataLink.FieldName;
end;
function TJDBLabeledDateEdit.GetDataSource: TDataSource;
begin
Result := FDataLink.DataSource;
end;
function TJDBLabeledDateEdit.GetField: TField;
begin
Result := FDataLink.Field;
end;
function TJDBLabeledDateEdit.IsReadOnly: boolean;
begin
if FDatalink.Active then
Result := not FDatalink.CanModify
else
Result := False;
end;
function TJDBLabeledDateEdit.getFormat: string;
begin
Result := fFormat;
end;
procedure TJDBLabeledDateEdit.setFormat(const AValue: string);
begin
fFormat := AValue;
if not Focused then
formatInput;
end;
procedure TJDBLabeledDateEdit.formatInput;
begin
if FDataLink.Field <> nil then
//FDataLink.Field.DisplayText -> formatted (tdbgridcolumns/persistent field DisplayFormat
if (fFormat <> '') and (not FDataLink.Field.IsNull) then
Caption := FormatDateTime(fFormat, FDataLink.Field.AsDateTime)
else
Caption := FDataLink.Field.DisplayText
else
Caption := 'nil';
end;
function TJDBLabeledDateEdit.GetReadOnly: boolean;
begin
Result := FDataLink.ReadOnly;
end;
procedure TJDBLabeledDateEdit.SetReadOnly(Value: boolean);
begin
inherited;
FDataLink.ReadOnly := Value;
end;
procedure TJDBLabeledDateEdit.SetParent(AParent: TWinControl);
begin
inherited SetParent(AParent);
if FButton <> nil then
begin
DoPositionButton;
CheckButtonVisible;
end;
end;
procedure TJDBLabeledDateEdit.DoPositionButton;
begin
if FButton = nil then
exit;
FButton.Parent := Parent;
FButton.Visible := True;
if BiDiMode = bdLeftToRight then
FButton.AnchorToCompanion(akLeft, 0, Self)
else
FButton.AnchorToCompanion(akRight, 0, Self);
end;
procedure TJDBLabeledDateEdit.CheckButtonVisible;
begin
if Assigned(FButton) then
FButton.Visible := True;
end;
procedure TJDBLabeledDateEdit.CMVisibleChanged(var Msg: TLMessage);
begin
inherited CMVisibleChanged(Msg);
CheckButtonVisible;
end;
procedure TJDBLabeledDateEdit.CMEnabledChanged(var Msg: TLMessage);
begin
inherited CMEnabledChanged(Msg);
if (FButton <> nil) then
FButton.Enabled := True;
end;
procedure TJDBLabeledDateEdit.CMBiDiModeChanged(var Message: TLMessage);
begin
inherited;
DoPositionButton;
end;
procedure TJDBLabeledDateEdit.ShowCalendar(Sender: TObject);
var
PopupOrigin: TPoint;
ADate: TDateTime;
begin
if (not Assigned(FDataLink.Field)) or IsReadOnly then
exit;
PopupOrigin := Self.ControlToScreen(Point(0, Self.Height));
if FDataLink.Field.IsNull then
ADate := now
else
ADate := FDataLink.Field.AsDateTime;
ShowCalendarPopup(PopupOrigin, ADate, [dsShowHeadings, dsShowDayNames],
@CalendarPopupReturnDate, nil);
end;
procedure TJDBLabeledDateEdit.CalendarPopupReturnDate(Sender: TObject;
const ADate: TDateTime);
var
bufdate: TDateTime;
begin
if not (DataSource.State in [dsEdit, dsInsert]) then
DataSource.Edit;
if FDataLink.Field.IsNull then
bufdate := now
else
bufdate := FDataLink.Field.AsDateTime;
FDataLink.Field.AsDateTime :=
EncodeDateTime(YearOf(ADate), MonthOf(ADate), DayOf(ADate),
HourOf(bufdate), MinuteOf(bufdate), SecondOf(bufdate), MilliSecondOf(bufdate));
end;
procedure TJDBLabeledDateEdit.SetDataField(const Value: string);
begin
FDataLink.FieldName := Value;
end;
procedure TJDBLabeledDateEdit.SetDataSource(Value: TDataSource);
begin
if not (FDataLink.DataSourceFixed and (csLoading in ComponentState)) then
ChangeDataSource(Self, FDataLink, Value);
end;
procedure TJDBLabeledDateEdit.CMGetDataLink(var Message: TLMessage);
begin
Message.Result := PtrUInt(FDataLink); // Delphi dbctrls compatibility?
end;
function TJDBLabeledDateEdit.IsValidCurrency(const Value: string): boolean;
begin
if StrToCurrDef(Value, MaxCurrency) = MaxCurrency then
Result := False
else
Result := True;
end;
procedure TJDBLabeledDateEdit.Loaded;
begin
inherited Loaded;
DoPositionButton;
CheckButtonVisible;
if (csDesigning in ComponentState) then
DataChange(Self);
end;
procedure TJDBLabeledDateEdit.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (AComponent = FButton) and (Operation = opRemove) then
FButton := nil;
// clean up
if (Operation = opRemove) then
begin
if (FDataLink <> nil) and (AComponent = DataSource) then
DataSource := nil;
end;
end;
procedure TJDBLabeledDateEdit.ActiveChange(Sender: TObject);
begin
if FDatalink.Active then
datachange(Sender)
else
Text := '';
end;
procedure TJDBLabeledDateEdit.KeyDown(var Key: word; Shift: TShiftState);
begin
inherited KeyDown(Key, Shift);
if (ssAlt in Shift) and (key = 40) then
ShowCalendar(Self);
if Key = VK_ESCAPE then
begin
FDataLink.Reset;
SelectAll;
Key := VK_UNKNOWN;
end
else
if Key in [VK_DELETE, VK_BACK] then
begin
if not IsReadOnly then
FDatalink.Edit
else
Key := VK_UNKNOWN;
end;
end;
procedure TJDBLabeledDateEdit.KeyPress(var Key: char);
begin
if not FieldIsEditable(Field) or not FDatalink.Edit then
Key := #0;
if not (Key in ['0'..'9', #8, #9, '.', '-', '/']) then
Key := #0;
inherited KeyPress(Key);
end;
procedure TJDBLabeledDateEdit.DoEnter;
begin
if not FieldIsEditable(Field) or IsReadOnly then
exit;
if FDataLink.Field <> nil then
Caption := FDataLink.Field.AsString;
inherited DoEnter;
end;
constructor TJDBLabeledDateEdit.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
ControlStyle := ControlStyle + [csReplicatable];
FDataLink := TFieldDataLink.Create;
FDataLink.Control := Self;
FDataLink.OnDataChange := @DataChange;
FDataLink.OnUpdateData := @UpdateData;
FDataLInk.OnActiveChange := @ActiveChange;
FButton := TSpeedButton.Create(self);
FButton.Height := Self.Height;
FButton.FreeNotification(Self);
CheckButtonVisible;
FButton.Cursor := crArrow;
FButton.Flat := False;
FButton.OnClick := @ShowCalendar;
FButton.ControlStyle := FButton.ControlStyle + [csNoDesignSelectable];
FButton.LoadGlyphFromLazarusResource('JCalendarIcon');
ControlStyle := ControlStyle - [csSetCaption];
end;
destructor TJDBLabeledDateEdit.Destroy;
begin
FreeAndNil(FDataLink);
FreeAndNil(FButton);
inherited Destroy;
end;
procedure TJDBLabeledDateEdit.EditingDone;
begin
if not FieldIsEditable(Field) or IsReadOnly then
exit;
if DataSource.State in [dsEdit, dsInsert] then
UpdateData(self)
else
formatInput;
inherited EditingDone;
end;
end.