{********************************************************************} { } { Developer Express Visual Component Library } { ExpressEditors } { } { Copyright (c) 1998-2008 Developer Express Inc. } { ALL RIGHTS RESERVED } { } { The entire contents of this file is protected by U.S. and } { International Copyright Laws. Unauthorized reproduction, } { reverse-engineering, and distribution of all or any portion of } { the code contained in this file is strictly prohibited and may } { result in severe civil and criminal penalties and will be } { prosecuted to the maximum extent possible under the law. } { } { RESTRICTIONS } { } { THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES } { (DCU, OBJ, DLL, ETC.) ARE CONFIDENTIAL AND PROPRIETARY TRADE } { SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS } { LICENSED TO DISTRIBUTE THE EXPRESSEDITORS AND ALL } { ACCOMPANYING VCL CONTROLS AS PART OF AN EXECUTABLE PROGRAM ONLY. } { } { THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED } { FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE } { COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE } { AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT } { AND PERMISSION FROM DEVELOPER EXPRESS INC. } { } { CONSULT THE END USER LICENSE AGREEMENT FOR INFORMATION ON } { ADDITIONAL RESTRICTIONS. } { } {********************************************************************} unit cxTimeEdit; {$I cxVer.inc} interface uses {$IFDEF DELPHI6} Variants, {$ENDIF} Windows, Classes, Controls, SysUtils, cxClasses, cxContainer, cxDataUtils, cxDateUtils, cxEdit, cxFormats, cxMaskEdit, cxSpinEdit, cxVariants, cxFilterControlUtils; type TcxTimeEditZoneKind = (tzHour, tzMin, tzSec, tzTimeSuffix); TcxTimeEditZoneInfo = record Kind: TcxTimeEditZoneKind; Start, Length: Integer; TimeSuffixKind: TcxTimeSuffixKind; Use24HourFormat: Boolean; end; { TcxTimeEditMaskMode } TcxTimeEditMaskMode = class(TcxMaskEditStandardMode) protected function GetBlank(APos: Integer): Char; override; end; { TcxCustomTimeEditProperties } TcxCustomTimeEdit = class; TcxTimeEditTimeFormat = (tfHourMinSec, tfHourMin, tfHour); TcxCustomTimeEditProperties = class(TcxCustomSpinEditProperties) private FAutoCorrectHours: Boolean; FShowDate: Boolean; FTimeFormat: TcxTimeEditTimeFormat; FUse24HourFormat: Boolean; FUseTimeFormatWhenUnfocused: Boolean; procedure SetAutoCorrectHours(Value: Boolean); procedure SetUse24HourFormat(Value: Boolean); procedure SetShowDate(Value: Boolean); procedure SetTimeFormat(Value: TcxTimeEditTimeFormat); procedure SetUseTimeFormatWhenUnfocused(Value: Boolean); protected function DefaultFocusedDisplayValue: TcxEditValue; override; function ExtendValueUpToBound: Boolean; override; procedure FormatChanged; override; function GetDisplayFormatOptions: TcxEditDisplayFormatOptions; override; function GetModeClass(AMaskKind: TcxEditMaskKind): TcxMaskEditCustomModeClass; override; procedure GetTimeZoneInfo(APos: Integer; out AInfo: TcxTimeEditZoneInfo); virtual; function IsDisplayValueNumeric: Boolean; override; function IsEditValueNumeric: Boolean; override; function PrepareValue(const AValue: TcxEditValue): Variant; override; function PreserveSelection: Boolean; override; function GetEditingPlace(APos: Integer): TcxTimeEditZoneKind; function GetTimePartLength(AKind: TcxTimeEditZoneKind): Integer; virtual; function GetTimePartPos(AKind: TcxTimeEditZoneKind): Integer; virtual; function GetTimeSuffixKind: TcxTimeSuffixKind; virtual; procedure UpdateEditMask; public constructor Create(AOwner: TPersistent); override; procedure Assign(Source: TPersistent); override; class function GetContainerClass: TcxContainerClass; override; function GetEditValueSource(AEditFocused: Boolean): TcxDataEditValueSource; override; function IsDisplayValueValid(var DisplayValue: TcxEditValue; AEditFocused: Boolean): Boolean; override; function IsEditValueValid(var EditValue: TcxEditValue; AEditFocused: Boolean): Boolean; override; procedure PrepareDisplayValue(const AEditValue: TcxEditValue; var DisplayValue: TcxEditValue; AEditFocused: Boolean); override; procedure ValidateDisplayValue(var ADisplayValue: TcxEditValue; var AErrorText: TCaption; var AError: Boolean; AEdit: TcxCustomEdit); override; // !!! property AutoCorrectHours: Boolean read FAutoCorrectHours write SetAutoCorrectHours default True; property ShowDate: Boolean read FShowDate write SetShowDate default False; property TimeFormat: TcxTimeEditTimeFormat read FTimeFormat write SetTimeFormat default tfHourMinSec; property Use24HourFormat: Boolean read FUse24HourFormat write SetUse24HourFormat default True; property UseTimeFormatWhenUnfocused: Boolean read FUseTimeFormatWhenUnfocused write SetUseTimeFormatWhenUnfocused default True; end; { TcxTimeEditProperties } TcxTimeEditProperties = class(TcxCustomTimeEditProperties) published property Alignment; property AssignedValues; property AutoCorrectHours; property AutoSelect; property BeepOnError; property Circular; property ClearKey; property HideSelection; property ImeMode; property ImeName; property ImmediatePost; property ReadOnly; property ShowDate; property TimeFormat; property UseCtrlIncrement; property UseLeftAlignmentOnEditing; property Use24HourFormat; property UseTimeFormatWhenUnfocused; property ValidateOnEnter; property OnChange; property OnEditValueChanged; property OnValidate; end; { TcxCustomTimeEdit } TcxCustomTimeEdit = class(TcxCustomSpinEdit) private FSavedDate: TDate; function GetProperties: TcxCustomTimeEditProperties; function GetActiveProperties: TcxCustomTimeEditProperties; function GetTime: TTime; procedure SetProperties(Value: TcxCustomTimeEditProperties); procedure SetTime(Value: TTime); protected function GetIncrement(AButton: TcxSpinEditButton): Double; override; function GetValue: Variant; override; function IncrementValueToStr(const AValue: TcxEditValue): string; override; procedure Initialize; override; function InternalGetEditingValue: TcxEditValue; override; function InternalGetText: string; override; procedure InternalSetDisplayValue(const Value: TcxEditValue); override; procedure InternalSetEditValue(const Value: TcxEditValue; AValidateEditValue: Boolean); override; function InternalSetText(const Value: string): Boolean; override; function IsValidChar(AChar: Char): Boolean; override; function IsCharValidForPos(var AChar: Char; APos: Integer): Boolean; override; procedure PropertiesChanged(Sender: TObject); override; procedure SetValue(const Value: Variant); override; function EditingPlace: TcxTimeEditZoneKind; public procedure Clear; override; class function GetPropertiesClass: TcxCustomEditPropertiesClass; override; function Increment(AButton: TcxSpinEditButton): Boolean; override; procedure PrepareEditValue(const ADisplayValue: TcxEditValue; out EditValue: TcxEditValue; AEditFocused: Boolean); override; property ActiveProperties: TcxCustomTimeEditProperties read GetActiveProperties; property Properties: TcxCustomTimeEditProperties read GetProperties write SetProperties; property Time: TTime read GetTime write SetTime stored False; end; { TcxTimeEdit } TcxTimeEdit = class(TcxCustomTimeEdit) private function GetActiveProperties: TcxTimeEditProperties; function GetProperties: TcxTimeEditProperties; procedure SetProperties(Value: TcxTimeEditProperties); public class function GetPropertiesClass: TcxCustomEditPropertiesClass; override; property ActiveProperties: TcxTimeEditProperties read GetActiveProperties; published property Anchors; property AutoSize; property BeepOnEnter; property Constraints; property DragMode; property EditValue; property Enabled; property ParentColor; property ParentFont; property ParentShowHint; property PopupMenu; property Properties: TcxTimeEditProperties read GetProperties write SetProperties; property ShowHint; property Style; property StyleDisabled; property StyleFocused; property StyleHot; property TabOrder; property TabStop; property Time; property Visible; property DragCursor; property DragKind; property ImeMode; property ImeName; property OnClick; {$IFDEF DELPHI5} property OnContextPopup; {$ENDIF} property OnDblClick; property OnDragDrop; property OnDragOver; property OnEditing; property OnEndDrag; property OnEnter; property OnExit; property OnKeyDown; property OnKeyPress; property OnKeyUp; property OnMouseDown; property OnMouseEnter; property OnMouseLeave; property OnMouseMove; property OnMouseUp; property OnStartDrag; property OnEndDock; property OnStartDock; end; { TcxFilterTimeEditHelper } TcxFilterTimeEditHelper = class(TcxFilterSpinEditHelper) protected class procedure InitializeEdit(AEdit: TcxCustomEdit; AEditProperties: TcxCustomEditProperties); override; public class function GetFilterEditClass: TcxCustomEditClass; override; end; function IsCharValidForTimeEdit(ATimeEdit: TcxCustomMaskEdit; var AChar: Char; APos: Integer; const ATimeZoneInfo: TcxTimeEditZoneInfo): Boolean; implementation uses StdCtrls, cxControls, cxEditConsts, cxTextEdit{$IFDEF DELPHI6}, DateUtils{$ENDIF}, Forms, Dialogs, Math; type { TcxTimeEditFormatListener } TcxTimeEditFormatListener = class(TcxInterfacedPersistent, IcxFormatControllerListener) protected procedure FormatChanged; public constructor Create(AOwner: TPersistent); override; destructor Destroy; override; end; var cxTimeEditHalfDayIncrement, cxTimeEditOneHourIncrement, cxTimeEditOneMinIncrement, cxTimeEditOneSecIncrement: Double; FTimeEditFormatListener: TcxTimeEditFormatListener; cxTimeEditFormats: array [TcxTimeEditTimeFormat, Boolean, 0 .. 1] of string = ( (('hh:nn:ss ampm', '00:00:00 LL;1;0'), ('hh:nn:ss', '00:00:00;1;0')), (('hh:nn ampm', '00:00 LL;1;0'), ('hh:nn', '00:00;1;0')), (('hh ampm', '00 LL;1;0'), ('hh', '00;1;0')) ); function EqualChars(C1, C2: Char): Boolean; begin Result := UpperCase(C1) = UpperCase(C2); end; function GetFirstDifferenceIndex(const S1, S2: string): Integer; var ACount, I: Integer; begin Result := -1; ACount := Min(Length(S1), Length(S2)); for I := 1 to ACount do if not EqualChars(S1[I], S2[I]) then begin Result := I; Break; end; if (Result = -1) and (Length(S1) <> Length(S2)) then Result := ACount + 1; end; procedure GetTimeSuffixes(ATimeSuffixKind: TcxTimeSuffixKind; out ATimeSuffix1, ATimeSuffix2: string); begin case ATimeSuffixKind of tskAP: begin ATimeSuffix1 := 'A'; ATimeSuffix2 := 'P'; end; tskAMPM: begin ATimeSuffix1 := 'AM'; ATimeSuffix2 := 'PM'; end; tskAMPMString: begin ATimeSuffix1 := TimeAMString; ATimeSuffix2 := TimePMString; end; end; end; function IsCharValidForTimeEdit(ATimeEdit: TcxCustomMaskEdit; var AChar: Char; APos: Integer; const ATimeZoneInfo: TcxTimeEditZoneInfo): Boolean; procedure GetTimeValueItemValueRange(out AMinValue, AMaxValue: Integer); begin AMinValue := 0; if ATimeZoneInfo.Kind = tzHour then if not ATimeZoneInfo.Use24HourFormat then begin AMinValue := 1; AMaxValue := 12; end else AMaxValue := 23 else AMaxValue := 59; end; procedure SetEditText(const AText: string; APos: Integer); var ASelStart: Integer; S: string; begin S := ATimeEdit.Text; Delete(S, APos, Length(AText)); Insert(AText, S, APos); ASelStart := ATimeEdit.SelStart; TCustomEdit(ATimeEdit.InnerControl).Text := S; ATimeEdit.SelStart := ASelStart; end; function CheckTimeSuffix: Boolean; var AFirstDifferenceIndex: Integer; ATimeSuffix, ATimeSuffix1, ATimeSuffix2: string; begin GetTimeSuffixes(ATimeZoneInfo.TimeSuffixKind, ATimeSuffix1, ATimeSuffix2); AFirstDifferenceIndex := ATimeZoneInfo.Start + GetFirstDifferenceIndex(ATimeSuffix1, ATimeSuffix2) - 1; if APos < AFirstDifferenceIndex then begin Result := EqualChars(AChar, ATimeSuffix1[APos - ATimeZoneInfo.Start + 1]); if Result then AChar := ATimeSuffix1[APos - ATimeZoneInfo.Start + 1]; end else if APos > AFirstDifferenceIndex then begin Result := EqualChars(AChar, ATimeEdit.Text[APos]); if Result then AChar := ATimeEdit.Text[APos]; end else begin Result := True; if EqualChars(AChar, ATimeSuffix1[APos - ATimeZoneInfo.Start + 1]) then ATimeSuffix := ATimeSuffix1 else if EqualChars(AChar, ATimeSuffix2[APos - ATimeZoneInfo.Start + 1]) then ATimeSuffix := ATimeSuffix2 else Result := False; if Result then begin AChar := #0; SetEditText(ATimeSuffix, ATimeZoneInfo.Start); ATimeEdit.SelStart := ATimeZoneInfo.Start + ATimeZoneInfo.Length; end; end; end; function CheckTimeValueItemFirstDigit(ADigit: Char): Boolean; var AMaxTimeZoneValue, AMinTimeZoneValue: Integer; begin Result := True; GetTimeValueItemValueRange(AMinTimeZoneValue, AMaxTimeZoneValue); if ADigit = '0' then begin SetEditText('0' + ATimeEdit.Text[APos], APos); ATimeEdit.SelStart := ATimeEdit.SelStart + 1; AChar := #0; end else if ADigit <= IntToStr(AMaxTimeZoneValue)[1] then begin SetEditText(ADigit + '0', APos); ATimeEdit.SelStart := ATimeEdit.SelStart + 1; AChar := #0; end else begin SetEditText('0', APos); ATimeEdit.SelStart := ATimeEdit.SelStart + 1; end end; function CheckTimeValueItemSecondDigit(ADigit: Char): Boolean; begin Result := ADigit <> '0'; if Result then begin SetEditText('0', APos - 1); Result := True; end; end; function CheckTimeValueItem: Boolean; var AMaxTimeZoneValue, AMinTimeZoneValue: Integer; S: string; begin S := Copy(ATimeEdit.Text, ATimeZoneInfo.Start, ATimeZoneInfo.Length); S[APos - ATimeZoneInfo.Start + 1] := AChar; GetTimeValueItemValueRange(AMinTimeZoneValue, AMaxTimeZoneValue); Result := (StrToInt(S) >= AMinTimeZoneValue) and (StrToInt(S) <= AMaxTimeZoneValue); if not Result then if APos = ATimeZoneInfo.Start then Result := CheckTimeValueItemFirstDigit(AChar) else Result := CheckTimeValueItemSecondDigit(AChar); end; begin if ATimeZoneInfo.Kind = tzTimeSuffix then Result := CheckTimeSuffix else Result := CheckTimeValueItem; end; function IsSpace(AChar: Char): Boolean; begin Result := AChar = ' '; end; procedure PrepareTimeEditMasks; var AMask: string; ATimeFormat: TcxTimeEditTimeFormat; I: Integer; S: string; begin S := FormatDateTime('hh:mm:ss AMPM', 1.5); AMask := ''; for I := 9 to Length(S) do if IsSpace(S[I]) then AMask := AMask + ' ' else AMask := AMask + 'c'; AMask := TrimRight(AMask); for ATimeFormat := tfHourMinSec to tfHour do begin S := cxTimeEditFormats[ATimeFormat, False, 1]; S := Copy(S, 1, (3 - Integer(ATimeFormat)) * 3 - 1); S := S + AMask + ';1;0'; cxTimeEditFormats[ATimeFormat, False, 1] := S; end; end; { TcxTimeEditFormatListener } constructor TcxTimeEditFormatListener.Create(AOwner: TPersistent); begin inherited Create(AOwner); cxFormatController.AddListener(Self); PrepareTimeEditMasks; end; destructor TcxTimeEditFormatListener.Destroy; begin cxFormatController.RemoveListener(Self); inherited Destroy; end; procedure TcxTimeEditFormatListener.FormatChanged; begin PrepareTimeEditMasks; end; { TcxTimeEditMaskMode } function TcxTimeEditMaskMode.GetBlank(APos: Integer): Char; begin if TcxCustomTimeEditProperties(Properties).GetEditingPlace(APos) = tzTimeSuffix then Result := #0 else Result := '0'; end; { TcxCustomTimeEditProperties } constructor TcxCustomTimeEditProperties.Create(AOwner: TPersistent); begin inherited Create(AOwner); FTimeFormat := tfHourMinSec; ValueType := vtFloat; FAutoCorrectHours := True; FUse24HourFormat := True; FUseTimeFormatWhenUnfocused := True; UpdateEditMask; IgnoreMaskBlank := True; Increment := 1; MinValue := 0; MaxValue := 24 * 60 * 60 - 1; AlwaysShowBlanksAndLiterals := True; end; procedure TcxCustomTimeEditProperties.Assign(Source: TPersistent); begin if Source is TcxCustomTimeEditProperties then begin BeginUpdate; try inherited Assign(Source); with Source as TcxCustomTimeEditProperties do begin Self.AutoCorrectHours := AutoCorrectHours; Self.ShowDate := ShowDate; Self.TimeFormat := TimeFormat; Self.Use24HourFormat := Use24HourFormat; Self.UseTimeFormatWhenUnfocused := UseTimeFormatWhenUnfocused; end finally EndUpdate; end end else inherited Assign(Source); end; class function TcxCustomTimeEditProperties.GetContainerClass: TcxContainerClass; begin Result := TcxTimeEdit; end; function TcxCustomTimeEditProperties.IsDisplayValueValid(var DisplayValue: TcxEditValue; AEditFocused: Boolean): Boolean; var AText: string; ADateTime: TDateTime; begin AText := VarToStr(DisplayValue); Result := True; try ADateTime := StrToDateTime(AText); DisplayValue := FormatDateTime(cxTimeEditFormats[TimeFormat, FUse24HourFormat, 0], ADateTime); DisplayValue := TrimRight(DisplayValue); except Result := False; end; end; function TcxCustomTimeEditProperties.IsEditValueValid(var EditValue: TcxEditValue; AEditFocused: Boolean): Boolean; begin if VarIsStr(EditValue) then Result := IsDisplayValueValid(EditValue, AEditFocused) else Result := VarIsNull(EditValue) or VarIsDate(EditValue) or VarIsNumericEx(EditValue); end; procedure TcxCustomTimeEditProperties.PrepareDisplayValue(const AEditValue: TcxEditValue; var DisplayValue: TcxEditValue; AEditFocused: Boolean); function InternalFormatDateTime(AValue: TcxEditValue): string; begin if AEditFocused or FUseTimeFormatWhenUnfocused and ((AValue = 0) or not ShowDate or (DateOf(AValue) = 0)) then Result := FormatDateTime(cxTimeEditFormats[TimeFormat, Use24HourFormat, 0], AValue) else if (AValue = 0) or not ShowDate or (DateOf(AValue) = 0) then Result := TimeToStr(AValue) else Result := DateTimeToStr(AValue); Result := TrimRight(Result); end; begin if VarIsNull(AEditValue) then if not AEditFocused then DisplayValue := '' else begin DisplayValue := FormatDateTime(cxTimeEditFormats[TimeFormat, FUse24HourFormat, 0], 0); DisplayValue := TrimRight(DisplayValue); end else if not VarIsDate(AEditValue) and not VarIsNumericEx(AEditValue) and not VarIsStr(AEditValue) then raise EConvertError.CreateFmt(cxGetResourceString(@cxSEditTimeConvertError), []) else try if VarIsStr(AEditValue) then DisplayValue := InternalFormatDateTime(StrToDateTime(AEditValue)) else DisplayValue := InternalFormatDateTime(AEditValue); except DisplayValue := InternalFormatDateTime(SysUtils.Now); end; end; procedure TcxCustomTimeEditProperties.ValidateDisplayValue( var ADisplayValue: TcxEditValue; var AErrorText: TCaption; var AError: Boolean; AEdit: TcxCustomEdit); var AIsUserErrorDisplayValue: Boolean; begin if AErrorText = '' then AErrorText := cxGetResourceString(@cxSEditTimeConvertError); DoValidate(ADisplayValue, AErrorText, AError, AEdit, AIsUserErrorDisplayValue); end; function TcxCustomTimeEditProperties.GetEditValueSource(AEditFocused: Boolean): TcxDataEditValueSource; begin Result := GetValueEditorEditValueSource(AEditFocused); end; function TcxCustomTimeEditProperties.DefaultFocusedDisplayValue: TcxEditValue; begin Result := FormatDateTime(cxTimeEditFormats[TimeFormat, FUse24HourFormat, 0], 0); Result := TrimRight(Result); end; function TcxCustomTimeEditProperties.ExtendValueUpToBound: Boolean; begin Result := False; end; procedure TcxCustomTimeEditProperties.FormatChanged; begin BeginUpdate; try UpdateEditMask; finally EndUpdate; end; end; function TcxCustomTimeEditProperties.GetDisplayFormatOptions: TcxEditDisplayFormatOptions; begin Result := [dfoSupports, dfoNoCurrencyValue]; end; function TcxCustomTimeEditProperties.GetModeClass( AMaskKind: TcxEditMaskKind): TcxMaskEditCustomModeClass; begin Result := TcxTimeEditMaskMode; end; procedure TcxCustomTimeEditProperties.GetTimeZoneInfo(APos: Integer; out AInfo: TcxTimeEditZoneInfo); begin AInfo.Kind := GetEditingPlace(APos); AInfo.Start := GetTimePartPos(AInfo.Kind); AInfo.Length := GetTimePartLength(AInfo.Kind); AInfo.TimeSuffixKind := GetTimeSuffixKind; AInfo.Use24HourFormat := Use24HourFormat; end; function TcxCustomTimeEditProperties.GetTimePartLength(AKind: TcxTimeEditZoneKind): Integer; begin Result := 2; end; function TcxCustomTimeEditProperties.GetTimePartPos(AKind: TcxTimeEditZoneKind): Integer; begin if AKind = tzTimeSuffix then Result := (3 - Integer(TimeFormat)) * 3 + 1 else Result := Integer(AKind) * 3 + 1; end; function TcxCustomTimeEditProperties.GetTimeSuffixKind: TcxTimeSuffixKind; begin Result := tskAMPMString; end; function TcxCustomTimeEditProperties.IsDisplayValueNumeric: Boolean; begin Result := False; end; function TcxCustomTimeEditProperties.IsEditValueNumeric: Boolean; begin Result := False; end; function TcxCustomTimeEditProperties.PrepareValue(const AValue: TcxEditValue): Variant; begin Result := 0; end; function TcxCustomTimeEditProperties.PreserveSelection: Boolean; begin Result := True; end; function TcxCustomTimeEditProperties.GetEditingPlace( APos: Integer): TcxTimeEditZoneKind; var ATimeStringLength: Integer; ATimeZoneChar: Char; S: string; begin ATimeStringLength := (3 - Integer(TimeFormat)) * 3 - 1; if (MaxLength > 0) and (APos > MaxLength) then APos := MaxLength; if (APos > ATimeStringLength) and not Use24HourFormat then begin Result := tzTimeSuffix; Exit; end; S := UpperCase(cxTimeEditFormats[TimeFormat, Use24HourFormat, 0]); if APos > Length(S) then APos := Length(S); ATimeZoneChar := S[APos]; if not ((ATimeZoneChar = 'H') or (ATimeZoneChar = 'N') or (ATimeZoneChar = 'S')) then begin Inc(APos); if APos > Length(S) then APos := Length(S); ATimeZoneChar := S[APos]; end; case ATimeZoneChar of 'H': Result := tzHour; 'N': Result := tzMin; else Result := tzSec; end; end; procedure TcxCustomTimeEditProperties.UpdateEditMask; begin EditMask := cxTimeEditFormats[FTimeFormat, FUse24HourFormat, 1]; end; procedure TcxCustomTimeEditProperties.SetAutoCorrectHours(Value: Boolean); begin if Value <> FAutoCorrectHours then FAutoCorrectHours := Value; end; procedure TcxCustomTimeEditProperties.SetUse24HourFormat(Value: Boolean); begin if Value <> FUse24HourFormat then begin FUse24HourFormat := Value; UpdateEditMask; Changed; end; end; procedure TcxCustomTimeEditProperties.SetShowDate(Value: Boolean); begin if Value <> FShowDate then begin FShowDate := Value; Changed; end; end; procedure TcxCustomTimeEditProperties.SetTimeFormat(Value: TcxTimeEditTimeFormat); begin if FTimeFormat <> Value then begin FTimeFormat := Value; UpdateEditMask; Changed; end; end; procedure TcxCustomTimeEditProperties.SetUseTimeFormatWhenUnfocused( Value: Boolean); begin if FUseTimeFormatWhenUnfocused <> Value then begin FUseTimeFormatWhenUnfocused := Value; Changed; end; end; { TcxCustomTimeEdit } procedure TcxCustomTimeEdit.Clear; begin Time := 0; end; class function TcxCustomTimeEdit.GetPropertiesClass: TcxCustomEditPropertiesClass; begin Result := TcxCustomTimeEditProperties; end; function TcxCustomTimeEdit.Increment(AButton: TcxSpinEditButton): Boolean; var ACursorPosition: Integer; ASelStart, ASelLength: Integer; begin ACursorPosition := 0; ASelLength := 0; if HandleAllocated then begin ASelStart := SelStart; if EditingPlace = tzTimeSuffix then begin ACursorPosition := (3 - Integer(ActiveProperties.TimeFormat)) * 3; while IsSpace(DisplayValue[ACursorPosition]) do Inc(ACursorPosition); if IsPM(TDateTime(Time)) then ASelLength := Length(TimeAMString) else ASelLength := Length(TimePMString); Dec(ACursorPosition); end else begin ASelLength := 1; case ASelStart of 0..1: ACursorPosition := 1; 2..4: ACursorPosition := 4; else ACursorPosition := 7; end; end; end; Result := inherited Increment(AButton); if HandleAllocated then SetSelection(ACursorPosition, ASelLength); end; procedure TcxCustomTimeEdit.PrepareEditValue(const ADisplayValue: TcxEditValue; out EditValue: TcxEditValue; AEditFocused: Boolean); function GetCorrectTimeSuffix(const ASuffix: string): string; var APos: Integer; begin APos := Pos(TimeAMString, ASuffix); if APos <> 0 then Result := TimeAMString else Result := TimePMString; end; var ATimeStringLength: Integer; AText: string; AValue: Double; begin AText := ADisplayValue; ATimeStringLength := (3 - Integer(ActiveProperties.TimeFormat)) * 3 - 1; if not ActiveProperties.Use24HourFormat then AText := Copy(AText, 1, ATimeStringLength); if Length(AText) = 2 then AText := AText + ':00'; // delphi bug if not ActiveProperties.Use24HourFormat then AText := AText + GetCorrectTimeSuffix( Copy(ADisplayValue, ATimeStringLength + 1, Length(ADisplayValue) - ATimeStringLength)); AValue := 0; try try if AText <> '' then AValue := StrToTime(AText); except AValue := 0; end finally AValue := FSavedDate + AValue * cxSign(FSavedDate); EditValue := AValue; end; if EditValue <> 0 then EditValue := VarAsType(EditValue, varDate); end; function TcxCustomTimeEdit.GetIncrement(AButton: TcxSpinEditButton): Double; var AEditingPlace: TcxTimeEditZoneKind; AIsForwardDirection: Boolean; begin Result := 0; AIsForwardDirection := AButton in [sebForward, sebFastForward]; AEditingPlace := EditingPlace; case AEditingPlace of tzTimeSuffix: begin Result := cxTimeEditHalfDayIncrement; if AIsForwardDirection and (Value >= cxTimeEditHalfDayIncrement) or not AIsForwardDirection and (Value < cxTimeEditHalfDayIncrement) then Result := -Result; end; tzHour: Result := cxTimeEditOneHourIncrement; tzMin: Result := cxTimeEditOneMinIncrement; tzSec: Result := cxTimeEditOneSecIncrement; end; if (AEditingPlace in [tzMin, tzSec]) and (AButton in [sebFastBackward, sebFastForward]) then Result := Result * 10; if not AIsForwardDirection then Result := -Result; end; function TcxCustomTimeEdit.GetValue: Variant; var AHours, ATimeSuffixLength, AValue: Integer; S: string; begin S := Text; AHours := StrToInt(Copy(S, 1, 2)); if not ActiveProperties.Use24HourFormat then AHours := AHours mod 12; AValue := AHours * 60 * 60; if ActiveProperties.TimeFormat <> tfHour then AValue := AValue + StrToInt(Copy(S, 4, 2)) * 60; if ActiveProperties.TimeFormat = tfHourMinSec then AValue := AValue + StrToInt(Copy(S, 7, 2)); ATimeSuffixLength := Length(TimePMString); if not ActiveProperties.Use24HourFormat and InternalCompareString(Copy(S, Length(S) - ATimeSuffixLength + 1, ATimeSuffixLength), TimePMString, True) then Inc(AValue, 12 * 60 * 60); Result := AValue; end; function TcxCustomTimeEdit.IncrementValueToStr(const AValue: TcxEditValue): string; var ATime: TDateTime; begin ATime := AValue; Result := inherited IncrementValueToStr(ATime * {$IFDEF DELPHI7}MSecsPerSec{$ELSE}1000{$ENDIF} / MSecsPerDay); end; procedure TcxCustomTimeEdit.Initialize; begin inherited Initialize; ControlStyle := ControlStyle - [csSetCaption]; end; function TcxCustomTimeEdit.InternalGetEditingValue: TcxEditValue; begin PrepareEditValue(Text, Result, True); end; function TcxCustomTimeEdit.InternalGetText: string; begin Result := DisplayValue; end; procedure TcxCustomTimeEdit.InternalSetDisplayValue(const Value: TcxEditValue); begin if ActiveProperties.Use24HourFormat or (VarToStr(Value) <> '') then inherited InternalSetDisplayValue(Value) else InnerTextEdit.EditValue := Value; end; procedure TcxCustomTimeEdit.InternalSetEditValue(const Value: TcxEditValue; AValidateEditValue: Boolean); var AValue: TcxEditValue; AIsEditValueValid: Boolean; begin AValue := Value; AIsEditValueValid := not AValidateEditValue or ActiveProperties.IsEditValueValid(AValue, InternalFocused); if VarIsNull(Value) or not AIsEditValueValid then FSavedDate := 0 else if VarIsStr(AValue) then FSavedDate := TDate(DateOf(StrToDateTime(Value))) else FSavedDate := TDate(DateOf(Value)); inherited InternalSetEditValue(Value, AValidateEditValue); end; function TcxCustomTimeEdit.InternalSetText(const Value: string): Boolean; begin Result := SetDisplayText(Value); end; function TcxCustomTimeEdit.IsValidChar(AChar: Char): Boolean; begin Result := True; end; function TcxCustomTimeEdit.IsCharValidForPos(var AChar: Char; APos: Integer): Boolean; var ATimeZoneInfo: TcxTimeEditZoneInfo; begin ActiveProperties.GetTimeZoneInfo(APos, ATimeZoneInfo); Result := IsCharValidForTimeEdit(Self, AChar, APos, ATimeZoneInfo); end; procedure TcxCustomTimeEdit.PropertiesChanged(Sender: TObject); begin if not Focused then DataBinding.UpdateDisplayValue; SelStart := Length(DisplayValue); inherited PropertiesChanged(Sender); end; procedure TcxCustomTimeEdit.SetValue(const Value: Variant); var ATime: TDateTime; begin ATime := Value; Time := ATime * {$IFDEF DELPHI7}MSecsPerSec{$ELSE}1000{$ENDIF} / MSecsPerDay; end; function TcxCustomTimeEdit.EditingPlace: TcxTimeEditZoneKind; begin Result := ActiveProperties.GetEditingPlace(SelStart + 1); end; function TcxCustomTimeEdit.GetProperties: TcxCustomTimeEditProperties; begin Result := TcxCustomTimeEditProperties(FProperties); end; function TcxCustomTimeEdit.GetActiveProperties: TcxCustomTimeEditProperties; begin Result := TcxCustomTimeEditProperties(InternalGetActiveProperties); end; function TcxCustomTimeEdit.GetTime: TTime; var AValue: TcxEditValue; begin Result := TTime(SysUtils.Now); try if Focused then begin PrepareEditValue(DisplayValue, AValue, True); Result := TTime(VarAsType(AValue, varDate)); end else if VarIsStr(EditValue) then Result := TTime(StrToDateTime(EditValue)) else begin Result := TTime(VarAsType(EditValue, varDate)); end; finally Result := Abs(Frac(Result)); end; end; procedure TcxCustomTimeEdit.SetProperties(Value: TcxCustomTimeEditProperties); begin FProperties.Assign(Value); end; procedure TcxCustomTimeEdit.SetTime(Value: TTime); begin InternalEditValue := VarToDateTime( Abs(Frac(Value)) * cxSign(FSavedDate) + FSavedDate); end; { TcxTimeEdit } class function TcxTimeEdit.GetPropertiesClass: TcxCustomEditPropertiesClass; begin Result := TcxTimeEditProperties; end; function TcxTimeEdit.GetActiveProperties: TcxTimeEditProperties; begin Result := TcxTimeEditProperties(InternalGetActiveProperties); end; function TcxTimeEdit.GetProperties: TcxTimeEditProperties; begin Result := TcxTimeEditProperties(FProperties); end; procedure TcxTimeEdit.SetProperties(Value: TcxTimeEditProperties); begin FProperties.Assign(Value); end; { TcxFilterTimeEditHelper } class procedure TcxFilterTimeEditHelper.InitializeEdit(AEdit: TcxCustomEdit; AEditProperties: TcxCustomEditProperties); begin inherited InitializeEdit(AEdit, AEditProperties); TcxTimeEdit(AEdit).Time := 0; end; class function TcxFilterTimeEditHelper.GetFilterEditClass: TcxCustomEditClass; begin Result := TcxTimeEdit; end; initialization cxTimeEditHalfDayIncrement := 12 * 60 * 60; cxTimeEditOneHourIncrement := 60 * 60; cxTimeEditOneMinIncrement := 60; cxTimeEditOneSecIncrement := 1; FTimeEditFormatListener := TcxTimeEditFormatListener.Create(nil); GetRegisteredEditProperties.Register(TcxTimeEditProperties, scxSEditRepositoryTimeItem); FilterEditsController.Register(TcxTimeEditProperties, TcxFilterTimeEditHelper); finalization FilterEditsController.Unregister(TcxTimeEditProperties, TcxFilterTimeEditHelper); FreeAndNil(FTimeEditFormatListener); end.