
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6940 8e941d3f-bd1b-0410-a28a-d453659cc2b4
879 lines
26 KiB
ObjectPascal
879 lines
26 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: JvErrorIndicator.pas, released on 2002-11-16.
|
|
|
|
The Initial Developer of the Original Code is Peter Thörnqvist <peter3 at sourceforge dot net>.
|
|
Portions created by Peter Thörnqvist are Copyright (C) 2002 Peter Thörnqvist .
|
|
All Rights Reserved.
|
|
|
|
Contributor(s):
|
|
|
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
|
located at http://jvcl.delphi-jedi.org
|
|
|
|
Known Issues:
|
|
* Setting AutoScroll to True for a form and displaying error icons beyond the form's right
|
|
edge can make the form's scrollbars "jump up and down"
|
|
* Resizing components while displaying error images, doesn't move the error image smoothly
|
|
(this is caused by the image being moved only when the BlinkThread triggers)
|
|
|
|
Description:
|
|
A component patterned on the ErrorProvider in .NET:
|
|
"Provides a user interface for indicating that a control
|
|
on a form has an error associated with it."
|
|
To set the error, use the Error property: an empty error string, removes the error image
|
|
|
|
-----------------------------------------------------------------------------}
|
|
// $Id$
|
|
|
|
unit JvErrorIndicator;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, Graphics, Controls, ImgList;
|
|
|
|
type
|
|
IJvErrorIndicatorClient = interface;
|
|
|
|
// IJvErrorIndicator is implemented by the TJvErrorIndicator
|
|
IJvErrorIndicator = interface
|
|
['{5BCB5404-9C17-4CC6-96EC-46567CA19A12}']
|
|
procedure BeginUpdate;
|
|
procedure EndUpdate;
|
|
procedure SetError(AControl: TControl; const AErrorMessage: WideString);
|
|
procedure SetClientError(const AClient: IJvErrorIndicatorClient);
|
|
end;
|
|
|
|
// IJvErrorIndicatorClient should be implemented by controls that wants to be able
|
|
// to update the error indicator through it's own properties
|
|
IJvErrorIndicatorClient = interface
|
|
['{9871F250-631E-4119-B073-71B28711C9B8}']
|
|
procedure SetErrorIndicator(const Value: IJvErrorIndicator);
|
|
function GetErrorIndicator: IJvErrorIndicator;
|
|
function GetControl: TControl;
|
|
procedure SetErrorMessage(const Value: WideString);
|
|
function GetErrorMessage: WideString;
|
|
|
|
property ErrorIndicator: IJvErrorIndicator read GetErrorIndicator write SetErrorIndicator;
|
|
property ErrorMessage: WideString read GetErrorMessage write SetErrorMessage;
|
|
end;
|
|
|
|
TJvErrorBlinkStyle = (ebsAlwaysBlink, ebsBlinkIfDifferentError, ebsNeverBlink);
|
|
TJvErrorImageAlignment = (eiaBottomLeft, eiaBottomRight, eiaMiddleLeft, eiaMiddleRight,
|
|
eiaTopLeft, eiaTopRight);
|
|
|
|
{ TJvErrorControl }
|
|
|
|
TJvErrorControl = class(TGraphicControl)
|
|
private
|
|
FImageList: TCustomImageList;
|
|
FImageIndex: Integer;
|
|
FImagePadding: Integer;
|
|
FControl: TControl;
|
|
FImageAlignment: TJvErrorImageAlignment;
|
|
FBlinkCount: Integer;
|
|
FUseAnchors: Boolean;
|
|
procedure SetError(const Value: string);
|
|
function GetError: string;
|
|
procedure SetImageIndex(const Value: Integer);
|
|
procedure SetImageList(const Value: TCustomImageList);
|
|
procedure SetControl(const Value: TControl);
|
|
procedure SetUseAnchors(AValue: Boolean);
|
|
protected
|
|
procedure Paint; override;
|
|
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
|
|
procedure UpdateAnchors;
|
|
public
|
|
function CalcBoundsRect: TRect;
|
|
property Images: TCustomImageList read FImageList write SetImageList;
|
|
property ImageIndex: Integer read FImageIndex write SetImageIndex;
|
|
property Control: TControl read FControl write SetControl;
|
|
property Error: string read GetError write SetError;
|
|
property BlinkCount: Integer read FBlinkCount write FBlinkCount;
|
|
property ImageAlignment: TJvErrorImageAlignment read FImageAlignment write FImageAlignment;
|
|
property ImagePadding: Integer read FImagePadding write FImagePadding;
|
|
property UseAnchors: Boolean read FUseAnchors write SetUseAnchors;
|
|
|
|
procedure DrawImage(Erase: Boolean);
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
property ShowHint default True;
|
|
property Width default 16;
|
|
property Height default 16;
|
|
end;
|
|
|
|
{ TJvErrorIndicator }
|
|
|
|
TJvErrorIndicator = class(TComponent, IUnknown, IJvErrorIndicator)
|
|
private
|
|
FDefaultUseAnchors: Boolean;
|
|
FUpdateCount: Integer;
|
|
FControls: TList;
|
|
FBlinkRate: Integer;
|
|
FImageList: TCustomImageList;
|
|
FBlinkThread: TThread;
|
|
FBlinkStyle: TJvErrorBlinkStyle;
|
|
FChangeLink: TChangeLink;
|
|
FImageIndex: Integer;
|
|
FDefaultImage: TImageList;
|
|
function GetError(AControl: TControl): string;
|
|
function GetImageAlignment(AControl: TControl): TJvErrorImageAlignment;
|
|
function GetImagePadding(AControl: TControl): Integer;
|
|
function GetUseAnchors(AControl: TControl): Boolean;
|
|
procedure SetBlinkRate(const Value: Integer);
|
|
procedure SetBlinkStyle(const Value: TJvErrorBlinkStyle);
|
|
procedure SetError(AControl: TControl; const Value: string);
|
|
procedure SetImageList(const Value: TCustomImageList);
|
|
procedure SetImageAlignment(AControl: TControl; const Value: TJvErrorImageAlignment);
|
|
procedure SetImagePadding(AControl: TControl; const Value: Integer);
|
|
procedure SetImageIndex(const Value: Integer);
|
|
procedure DoChangeLinkChange(Sender: TObject);
|
|
procedure DoBlink(Sender: TObject; Erase: Boolean);
|
|
procedure SetUseAnchors(AControl: TControl; AValue: Boolean);
|
|
procedure StopThread;
|
|
procedure StartThread;
|
|
function GetControl(Index: Integer): TJvErrorControl;
|
|
function GetCount: Integer;
|
|
protected
|
|
{ IJvErrorIndicator }
|
|
procedure IJvErrorIndicator.SetError = IndicatorSetError;
|
|
procedure IndicatorSetError(AControl: TControl; const ErrorMessage: WideString);
|
|
procedure SetClientError(const AClient: IJvErrorIndicatorClient);
|
|
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
|
|
function IndexOf(AControl: TControl): Integer;
|
|
function Add(AControl: TControl): Integer;
|
|
procedure UpdateControls;
|
|
procedure Delete(Index: Integer);
|
|
property Controls[Index: Integer]: TJvErrorControl read GetControl;
|
|
property Count: Integer read GetCount;
|
|
public
|
|
constructor Create(AComponent: TComponent); override;
|
|
destructor Destroy; override;
|
|
|
|
// Call ClearErrors to remove all error images with one call
|
|
// After a call to ClearErrors, the internal error image list is emptied
|
|
// Calling ClearErrors is the same as setting Error[nil] := '' but is slightly faster
|
|
procedure ClearErrors;
|
|
// The BeginUpdate method suspends the blinking thread until the EndUpdate method is called.
|
|
procedure BeginUpdate;
|
|
// EndUpdate re-enables the blinking thread that was turned off with the BeginUpdate method.
|
|
procedure EndUpdate;
|
|
// Gets or sets the error message associated with a control
|
|
// Setting the error message to an empty string removes the error image
|
|
// (this is the only way to remove an error image for a single control)
|
|
// Use Error[nil] := 'SomeValue'; to assign the error message 'SomeValue' to all controls
|
|
// Using Error[nil] := ''; is equivalent to calling ClearErrors but ClearErrors is faster
|
|
property Error[AControl: TControl]: string read GetError write SetError;
|
|
// Gets or sets a value indicating where the error image should be placed in relation to the control.
|
|
// The location can be further modified by assigning a non-zero value to ImagePadding
|
|
// Possible values:
|
|
// eiaBottomLeft - display the error image on the controls left side aligned to the bottom edge of the control
|
|
// eiaBottomRight - display the error image on the controls right side aligned to the bottom edge of the control
|
|
// eiaMiddleLeft - display the error image on the controls left side aligned to the middle of the control
|
|
// eiaMiddleRight - display the error image on the controls right side aligned to the middle of the control
|
|
// eiaTopLeft - display the error image on the controlsleft side aligned to the top edge of the control
|
|
// eiaTopRight - display the error image on the controls right side aligned to the top edge of the control
|
|
// Use AControl = nil to set the same Alignment for all controls
|
|
property ImageAlignment[AControl: TControl]: TJvErrorImageAlignment read GetImageAlignment write SetImageAlignment;
|
|
// Gets or sets the amount of extra space to leave between the specified control and the error image.
|
|
// Use AControl = nil to set the same padding for all controls.
|
|
property ImagePadding[AControl: TControl]: Integer read GetImagePadding write SetImagePadding;
|
|
//
|
|
property UseAnchors[AControl: TControl]: Boolean read GetUseAnchors write SetUseAnchors;
|
|
published
|
|
// The rate at which the error image should flash. The rate is expressed in milliseconds. The default is 250 milliseconds.
|
|
// A value of zero sets BlinkStyle to ebsNeverBlink.
|
|
property BlinkRate: Integer read FBlinkRate write SetBlinkRate default 250;
|
|
// The error Image flashes in the manner specified by the assigned BlinkStyle when an error occurs.
|
|
// Possible values:
|
|
// ebsBlinkIfDifferentError - blink if the new error message differs from the previous
|
|
// ebsAlwaysBlink - always blink when the error message changes, even if it's the same message
|
|
// ebsNeverBlink - never bink, just display the error image and the description
|
|
// Setting the BlinkRate to zero sets the BlinkStyle to ebsNeverBlink.
|
|
// The default is ebsBlinkIfDifferentError
|
|
property BlinkStyle: TJvErrorBlinkStyle read FBlinkStyle write SetBlinkStyle default ebsBlinkIfDifferentError;
|
|
// Gets or sets the ImageList where to retrieve an image to display next to a control when an error description
|
|
// string has been set for the control.
|
|
// This property is used in conjunction with ImageIndex to select the image to display
|
|
// If either is nil, invalid or out of range, no error image is displayed
|
|
property Images: TCustomImageList read FImageList write SetImageList;
|
|
// Gets or sets the ImageIndex in ImageList to use when displaying an image next to a control
|
|
property ImageIndex: Integer read FImageIndex write SetImageIndex;
|
|
property DefaultUseAnchors: Boolean read FDefaultUseAnchors write FDefaultUseAnchors;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
//CommCtrl,
|
|
LCLProc,
|
|
SysUtils,
|
|
JvResources, JvJVCLUtils;
|
|
|
|
{$R ..\..\resource\JvErrorIndicator.res}
|
|
|
|
const
|
|
cDefBlinkCount = 5;
|
|
|
|
type
|
|
TJvBlinkThreadEvent = procedure(Sender: TObject; Erase: Boolean) of object;
|
|
|
|
TJvBlinkThread = class(TThread)
|
|
private
|
|
FBlinkRate: Integer;
|
|
FErase: Boolean;
|
|
FOnBlink: TJvBlinkThreadEvent;
|
|
procedure Blink;
|
|
protected
|
|
procedure Execute; override;
|
|
public
|
|
constructor Create(BlinkRate: Integer; AOnBlink: TJvBlinkThreadEvent);
|
|
end;
|
|
|
|
//=== { TJvErrorIndicator } ==================================================
|
|
|
|
constructor TJvErrorIndicator.Create(AComponent: TComponent);
|
|
|
|
begin
|
|
inherited Create(AComponent);
|
|
FDefaultImage := TImageList.CreateSize(16, 16);
|
|
FDefaultImage.AddResourceName(HINSTANCE, 'XJVERRORINDICATORICON');
|
|
//ImageList_AddIcon(FDefaultImage.Handle,
|
|
// LoadImage(HInstance, PChar('XJVERRORINDICATORICON'), IMAGE_ICON, 16, 16, 0));
|
|
FBlinkStyle := ebsBlinkIfDifferentError;
|
|
FBlinkRate := 250;
|
|
FControls := TList.Create;
|
|
FChangeLink := TChangeLink.Create;
|
|
FChangeLink.OnChange := @DoChangeLinkChange;
|
|
end;
|
|
|
|
destructor TJvErrorIndicator.Destroy;
|
|
begin
|
|
StopThread;
|
|
ClearErrors;
|
|
FControls.Free;
|
|
FChangeLink.Free;
|
|
FDefaultImage.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TJvErrorIndicator.Add(AControl: TControl): Integer;
|
|
var
|
|
Ci: TJvErrorControl;
|
|
begin
|
|
Result := IndexOf(AControl);
|
|
if (Result < 0) and (AControl <> nil) then
|
|
begin
|
|
Ci := TJvErrorControl.Create(Self);
|
|
Ci.Control := AControl;
|
|
Ci.UseAnchors := DefaultUseAnchors;
|
|
// Ci.Name := Ci.Control.Name + '_ErrorControl';
|
|
Result := FControls.Add(Ci);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.Delete(Index: Integer);
|
|
begin
|
|
Controls[Index].Free; // removes itself from FControls[]
|
|
end;
|
|
|
|
function TJvErrorIndicator.GetError(AControl: TControl): string;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
I := IndexOf(AControl);
|
|
if I > -1 then
|
|
Result := Controls[I].Error
|
|
else
|
|
raise Exception.Create(RsEControlNotFoundInGetError);
|
|
end;
|
|
|
|
function TJvErrorIndicator.GetImageAlignment(AControl: TControl): TJvErrorImageAlignment;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
I := IndexOf(AControl);
|
|
if I > -1 then
|
|
Result := Controls[I].ImageAlignment
|
|
else
|
|
raise Exception.Create(RsEControlNotFoundInGetImageAlignment);
|
|
end;
|
|
|
|
function TJvErrorIndicator.GetImagePadding(AControl: TControl): Integer;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
I := IndexOf(AControl);
|
|
if I > -1 then
|
|
Result := Controls[I].ImagePadding
|
|
else
|
|
raise Exception.Create(RsEControlNotFoundInGetImagePadding);
|
|
end;
|
|
|
|
function TJvErrorIndicator.GetUseAnchors(AControl: TControl): Boolean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
I := IndexOf(AControl);
|
|
if I > -1 then
|
|
Result := Controls[I].UseAnchors
|
|
else
|
|
raise Exception.Create(RsEControlNotFoundInGetUseAnhors);
|
|
end;
|
|
|
|
function TJvErrorIndicator.IndexOf(AControl: TControl): Integer;
|
|
begin
|
|
if AControl <> nil then
|
|
for Result := 0 to Count - 1 do
|
|
if Controls[Result].Control = AControl then
|
|
Exit;
|
|
Result := -1;
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.Notification(AComponent: TComponent;
|
|
Operation: TOperation);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
inherited Notification(AComponent, Operation);
|
|
if Operation = opRemove then
|
|
begin
|
|
if AComponent is TControl then
|
|
I := IndexOf(TControl(AComponent))
|
|
else
|
|
I := -1;
|
|
if I > -1 then
|
|
Delete(I);
|
|
if AComponent = Images then
|
|
Images := nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.SetBlinkRate(const Value: Integer);
|
|
begin
|
|
if FBlinkRate <> Value then
|
|
begin
|
|
StopThread;
|
|
FBlinkRate := Value;
|
|
if FBlinkRate <= 0 then
|
|
begin
|
|
FBlinkRate := 0;
|
|
FBlinkStyle := ebsNeverBlink;
|
|
end;
|
|
UpdateControls;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.SetBlinkStyle(const Value: TJvErrorBlinkStyle);
|
|
begin
|
|
if FBlinkStyle <> Value then
|
|
begin
|
|
StopThread;
|
|
FBlinkStyle := Value;
|
|
UpdateControls;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.SetError(AControl: TControl;
|
|
const Value: string);
|
|
var
|
|
I: Integer;
|
|
Ei: TJvErrorControl;
|
|
begin
|
|
StopThread;
|
|
if AControl = nil then
|
|
begin
|
|
if Value = '' then
|
|
ClearErrors
|
|
else
|
|
for I := 0 to Count - 1 do
|
|
begin
|
|
Ei := Controls[I];
|
|
if ((Ei.Error <> Value) and (BlinkStyle = ebsBlinkIfDifferentError)) or (BlinkStyle = ebsAlwaysBlink) then
|
|
Ei.BlinkCount := cDefBlinkCount
|
|
else
|
|
if BlinkStyle = ebsNeverBlink then
|
|
Ei.BlinkCount := 0;
|
|
Ei.Error := Value;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
I := Add(AControl);
|
|
if I > -1 then
|
|
begin
|
|
if Value = '' then
|
|
Delete(I)
|
|
else
|
|
begin
|
|
Ei := Controls[I];
|
|
if ((Ei.Error <> Value) and (BlinkStyle = ebsBlinkIfDifferentError)) or
|
|
(BlinkStyle = ebsAlwaysBlink) then
|
|
begin
|
|
Ei.Error := Value;
|
|
Ei.BlinkCount := cDefBlinkCount;
|
|
Ei.Visible := (csDesigning in ComponentState);
|
|
if (FUpdateCount = 0) and (FBlinkThread = nil) then
|
|
StartThread;
|
|
end
|
|
else
|
|
if BlinkStyle = ebsNeverBlink then
|
|
begin
|
|
Ei.BlinkCount := 0;
|
|
Ei.Error := Value;
|
|
Ei.Visible := (Value <> '');
|
|
end;
|
|
end;
|
|
UpdateControls;
|
|
end
|
|
else
|
|
raise Exception.Create(RsEUnableToAddControlInSetError);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.SetImageAlignment(AControl: TControl;
|
|
const Value: TJvErrorImageAlignment);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if AControl = nil then
|
|
for I := 0 to Count - 1 do
|
|
Controls[I].ImageAlignment := Value
|
|
else
|
|
begin
|
|
I := Add(AControl);
|
|
if I > -1 then
|
|
Controls[I].ImageAlignment := Value
|
|
else
|
|
raise Exception.Create(RsEUnableToAddControlInSetImageAlignme);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.SetImagePadding(AControl: TControl;
|
|
const Value: Integer);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if AControl = nil then
|
|
for I := 0 to Count - 1 do
|
|
Controls[I].ImagePadding := Value
|
|
else
|
|
begin
|
|
I := Add(AControl);
|
|
if I > -1 then
|
|
Controls[I].ImagePadding := Value
|
|
else
|
|
raise Exception.Create(RsEUnableToAddControlInSetImagePadding);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.UpdateControls;
|
|
var
|
|
I, J: Integer;
|
|
IL: TCustomImageList;
|
|
begin
|
|
if Images <> nil then
|
|
begin
|
|
IL := Images;
|
|
J := ImageIndex;
|
|
end
|
|
else
|
|
begin
|
|
IL := FDefaultImage;
|
|
J := 0;
|
|
end;
|
|
for I := 0 to Count - 1 do
|
|
begin
|
|
Controls[I].Images := IL;
|
|
Controls[I].ImageIndex := J;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.SetImageList(const Value: TCustomImageList);
|
|
begin
|
|
if FImageList <> Value then
|
|
begin
|
|
StopThread;
|
|
ReplaceImageListReference(Self, Value, FImageList, FChangeLink);
|
|
UpdateControls;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.SetImageIndex(const Value: Integer);
|
|
begin
|
|
if FImageIndex <> Value then
|
|
begin
|
|
StopThread;
|
|
FImageIndex := Value;
|
|
UpdateControls;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.DoChangeLinkChange(Sender: TObject);
|
|
begin
|
|
UpdateControls;
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.ClearErrors;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
StopThread;
|
|
for I := Count - 1 downto 0 do
|
|
Controls[I].Free;
|
|
FControls.Clear;
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.BeginUpdate;
|
|
{var
|
|
I: Integer;}
|
|
begin
|
|
Inc(FUpdateCount);
|
|
StopThread;
|
|
// ahuser: The following code produces flicker
|
|
{for I := 0 to Count - 1 do
|
|
Controls[I].Visible := False;}
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.EndUpdate;
|
|
begin
|
|
if FUpdateCount > 0 then
|
|
begin
|
|
Dec(FUpdateCount);
|
|
if FUpdateCount = 0 then
|
|
begin
|
|
UpdateControls;
|
|
StartThread;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.StartThread;
|
|
begin
|
|
if BlinkStyle <> ebsNeverBlink then
|
|
FBlinkThread := TJvBlinkThread.Create(BlinkRate, @DoBlink);
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.StopThread;
|
|
begin
|
|
if FBlinkThread <> nil then
|
|
try
|
|
FBlinkThread.Terminate;
|
|
FBlinkThread.WaitFor;
|
|
finally
|
|
FreeAndNil(FBlinkThread);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.DoBlink(Sender: TObject; Erase: Boolean);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := 0 to Count - 1 do
|
|
Controls[I].DrawImage(Erase);
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.SetUseAnchors(AControl: TControl; AValue: Boolean);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if AControl = nil then
|
|
for I := 0 to Count - 1 do
|
|
Controls[I].UseAnchors := AValue
|
|
else
|
|
begin
|
|
I := Add(AControl);
|
|
if I > -1 then
|
|
Controls[I].UseAnchors := AValue
|
|
else
|
|
raise Exception.Create(RsEUnableToAddControlInSetImagePadding);
|
|
end;
|
|
end;
|
|
|
|
function TJvErrorIndicator.GetControl(Index: Integer): TJvErrorControl;
|
|
begin
|
|
Result := TJvErrorControl(FControls[Index]);
|
|
end;
|
|
|
|
function TJvErrorIndicator.GetCount: Integer;
|
|
begin
|
|
Result := FControls.Count;
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.SetClientError(const AClient: IJvErrorIndicatorClient);
|
|
begin
|
|
if AClient <> nil then
|
|
SetError(AClient.GetControl, UTF8Encode(AClient.ErrorMessage));
|
|
end;
|
|
|
|
procedure TJvErrorIndicator.IndicatorSetError(AControl: TControl;
|
|
const ErrorMessage: WideString);
|
|
begin
|
|
SetError(AControl, UTF8Encode(ErrorMessage));
|
|
end;
|
|
|
|
//=== { TJvErrorControl } ====================================================
|
|
|
|
constructor TJvErrorControl.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FImageAlignment := eiaMiddleRight;
|
|
ShowHint := True;
|
|
Visible := False;
|
|
Width := 16;
|
|
Height := 16;
|
|
end;
|
|
|
|
destructor TJvErrorControl.Destroy;
|
|
begin
|
|
TJvErrorIndicator(Owner).FControls.Extract(Self);
|
|
Control := nil;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TJvErrorControl.DrawImage(Erase: Boolean);
|
|
begin
|
|
if not Assigned(Control) or not Assigned(Control.Parent) or not Assigned(Images) then
|
|
Exit;
|
|
Visible := (Error <> '') and (not Erase or (BlinkCount < 2));
|
|
if not Visible and (BlinkCount > 1) then
|
|
Dec(FBlinkCount);
|
|
if Visible then
|
|
if UseAnchors then
|
|
UpdateAnchors
|
|
else
|
|
BoundsRect := CalcBoundsRect;
|
|
end;
|
|
|
|
function TJvErrorControl.CalcBoundsRect: TRect;
|
|
begin
|
|
if (Control = nil) or (Images = nil) then
|
|
Result := Rect(0, 0, 0, 0)
|
|
else
|
|
begin
|
|
case ImageAlignment of
|
|
eiaBottomLeft:
|
|
begin
|
|
// must qualify Result fully since Delphi confuses the TRect with the controls Top/Left properties
|
|
Result.Right := Control.Left - 1;
|
|
Result.Left := Result.Right - Images.Width;
|
|
Result.Bottom := Control.Top + Control.Height;
|
|
Result.Top := Result.Bottom - Images.Height;
|
|
OffsetRect(Result, -ImagePadding, 0);
|
|
end;
|
|
eiaBottomRight:
|
|
begin
|
|
Result.Left := Control.Left + Control.Width + 1;
|
|
Result.Right := Result.Left + Images.Width;
|
|
Result.Bottom := Control.Top + Control.Height;
|
|
Result.Top := Result.Bottom - Images.Height;
|
|
OffsetRect(Result, ImagePadding, 0);
|
|
end;
|
|
eiaMiddleLeft:
|
|
begin
|
|
Result.Right := Control.Left - 1;
|
|
Result.Left := Result.Right - Images.Width;
|
|
Result.Top := Control.Top + (Control.Height - Images.Height) div 2;
|
|
Result.Bottom := Result.Top + Images.Height;
|
|
OffsetRect(Result, -ImagePadding, 0);
|
|
end;
|
|
eiaMiddleRight:
|
|
begin
|
|
Result.Left := Control.Left + Control.Width + 1;
|
|
Result.Right := Result.Left + Images.Width;
|
|
Result.Top := Control.Top + (Control.Height - Images.Height) div 2;
|
|
Result.Bottom := Result.Top + Images.Height;
|
|
OffsetRect(Result, ImagePadding, 0);
|
|
end;
|
|
eiaTopLeft:
|
|
begin
|
|
Result.Right := Control.Left - 1;
|
|
Result.Left := Result.Right - Images.Width;
|
|
Result.Top := Control.Top;
|
|
Result.Bottom := Result.Top + Control.Height;
|
|
OffsetRect(Result, -ImagePadding, 0);
|
|
end;
|
|
eiaTopRight:
|
|
begin
|
|
Result.Left := Control.Left + Control.Width + 1;
|
|
Result.Right := Result.Left + Images.Width;
|
|
Result.Top := Control.Top;
|
|
Result.Bottom := Result.Top + Images.Height;
|
|
OffsetRect(Result, ImagePadding, 0);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvErrorControl.Paint;
|
|
begin
|
|
// inherited Paint;
|
|
if (Images <> nil) and Visible then
|
|
Images.Draw(Canvas, 0, 0, ImageIndex, dsTransparent, itImage);
|
|
end;
|
|
|
|
procedure TJvErrorControl.SetError(const Value: string);
|
|
begin
|
|
Hint := Value;
|
|
end;
|
|
|
|
function TJvErrorControl.GetError: string;
|
|
begin
|
|
Result := Hint;
|
|
end;
|
|
|
|
procedure TJvErrorControl.SetImageIndex(const Value: Integer);
|
|
begin
|
|
if FImageIndex <> Value then
|
|
begin
|
|
FImageIndex := Value;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvErrorControl.SetImageList(const Value: TCustomImageList);
|
|
begin
|
|
if ReplaceComponentReference(Self, Value, TComponent(FImageList)) then
|
|
begin
|
|
if FImageList <> nil then
|
|
if UseAnchors then
|
|
UpdateAnchors
|
|
else
|
|
BoundsRect := CalcBoundsRect
|
|
else
|
|
SetBounds(Left, Top, 16, 16);
|
|
// Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvErrorControl.SetControl(const Value: TControl);
|
|
begin
|
|
if FControl <> Value then
|
|
begin
|
|
ReplaceComponentReference(Self, Value, TComponent(FControl));
|
|
if FControl <> nil then
|
|
Parent := FControl.Parent
|
|
else
|
|
Parent := nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvErrorControl.SetUseAnchors(AValue: Boolean);
|
|
begin
|
|
if FUseAnchors = AValue then Exit;
|
|
FUseAnchors := AValue;
|
|
end;
|
|
|
|
procedure TJvErrorControl.Notification(AComponent: TComponent;
|
|
Operation: TOperation);
|
|
begin
|
|
inherited Notification(AComponent, Operation);
|
|
if (Operation = opRemove) then
|
|
if (AComponent = Control) then
|
|
Control := nil
|
|
else if (AComponent = FImageList) then
|
|
FImageList := nil
|
|
end;
|
|
|
|
procedure TJvErrorControl.UpdateAnchors;
|
|
begin
|
|
if (Control = nil) or (Images = nil) then
|
|
begin
|
|
SetBounds(0, 0, 0, 0);
|
|
Exit;
|
|
end
|
|
else
|
|
begin
|
|
AnchorSide[akLeft].Control := nil;
|
|
AnchorSide[akTop].Control := nil;
|
|
AnchorSide[akBottom].Control := nil;
|
|
AnchorSide[akRight].Control := nil;
|
|
SetBounds(0, 0, Images.Width, Images.Height);
|
|
case ImageAlignment of
|
|
eiaBottomLeft:
|
|
begin
|
|
AnchorSideLeft.Control := Control;
|
|
AnchorSideLeft.Side := asrLeft;
|
|
AnchorSideTop.Control := Control;
|
|
AnchorSideTop.Side := asrBottom;
|
|
end;
|
|
eiaBottomRight:
|
|
begin
|
|
AnchorSideRight.Control := Control;
|
|
AnchorSideRight.Side := asrRight;
|
|
AnchorSideTop.Control := Control;
|
|
AnchorSideTop.Side := asrBottom;
|
|
end;
|
|
eiaMiddleLeft:
|
|
begin
|
|
AnchorVerticalCenterTo(Control);
|
|
AnchorSideRight.Control := Control;
|
|
AnchorSideRight.Side := asrLeft;
|
|
end;
|
|
eiaMiddleRight:
|
|
begin
|
|
AnchorVerticalCenterTo(Control);
|
|
AnchorSideLeft.Control := Control;
|
|
AnchorSideLeft.Side := asrRight;
|
|
end;
|
|
eiaTopLeft:
|
|
begin
|
|
AnchorSideLeft.Control := Control;
|
|
AnchorSideLeft.Side := asrLeft;
|
|
AnchorSideBottom.Control := Control;
|
|
AnchorSideBottom.Side := asrTop;
|
|
end;
|
|
eiaTopRight:
|
|
begin
|
|
AnchorSideRight.Control := Control;
|
|
AnchorSideRight.Side := asrRight;
|
|
AnchorSideBottom.Control := Control;
|
|
AnchorSideBottom.Side := asrTop;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//=== { TJvBlinkThread } =====================================================
|
|
|
|
constructor TJvBlinkThread.Create(BlinkRate: Integer; AOnBlink: TJvBlinkThreadEvent);
|
|
begin
|
|
inherited Create(False);
|
|
FBlinkRate := BlinkRate;
|
|
FErase := False;
|
|
FOnBlink := AOnBlink;
|
|
end;
|
|
|
|
procedure TJvBlinkThread.Blink;
|
|
begin
|
|
if Assigned(FOnBlink) then
|
|
FOnBlink(Self, FErase);
|
|
end;
|
|
|
|
procedure TJvBlinkThread.Execute;
|
|
begin
|
|
//NameThread(ThreadName);
|
|
FErase := False;
|
|
while not Terminated and not Suspended do
|
|
begin
|
|
Sleep(FBlinkRate);
|
|
Synchronize(@Blink);
|
|
if FBlinkRate = 0 then
|
|
Exit;
|
|
FErase := not FErase;
|
|
end;
|
|
end;
|
|
|
|
end.
|