{****************************************************
 This file is part of the Eye Candy Controls (EC-C)
 
  Copyright (C) 2013 Vojtěch Čihák, Czech Republic
 
  This library is free software.

  See the file COPYING.LGPL.txt, 
  included in this distribution,
  for details about the license.
****************************************************}   

unit ECEditBtns;
{$mode objfpc}{$H+}  

//{$DEFINE DBGCTRLS}  //don't remove, just comment

interface

uses
  Classes, SysUtils, Controls, Graphics, Math, Themes, Types,
  StdCtrls, ActnList, CustomTimer, Dialogs, Forms, ImgList, Menus, 
  LCLProc, LCLType, LMessages, LResources, ECTypes, LCLIntf;

type
  {$PACKENUM 2}
  TEBOption = (eboClickAltEnter, eboClickCtrlEnter, eboClickShiftEnter);
  TEBOptions = set of TEBOption;   
  TItemOrder = (eioFixed, eioHistory, eioSorted);
  { Event }
  TOnDrawGlyph = procedure(Sender: TObject; AState: TItemState) of object;
  
const
  cDefEBOptions = [eboClickAltEnter, eboClickCtrlEnter, eboClickShiftEnter];

type 
  TCustomECSpeedBtn = class;

  { TECSpeedBtnActionLink }
  TECSpeedBtnActionLink = class(TWinControlActionLink)
  protected
    FClientSpeedBtn: TCustomECSpeedBtn;
    procedure AssignClient(AClient: TObject); override;
    procedure SetChecked(Value: Boolean); override;
  public
    function IsCheckedLinked: Boolean; override;
  end;

  TECSpeedBtnActionLinkClass = class of TECSpeedBtnActionLink;    
  
  { TCustomECSpeedBtn }
  TCustomECSpeedBtn = class(TCustomControl)
  private
    FAllowAllUp: Boolean;
    FChecked: Boolean;
    FCheckFromAction: Boolean;
    FDelay: Integer;
    FGlyphColor: TColor;
    FGlyphDesignChecked: TGlyphDesign;
    FGlyphDesign: TGlyphDesign;
    FGroupIndex: Integer;
    FImageIndex: SmallInt;
    FImageIndexChecked: SmallInt;
    FImages: TCustomImageList;
    FLayout: TObjectPos;
    FMargin: SmallInt;
    FSpacing: SmallInt;
    FOnChange: TNotifyEvent;
    FOnDrawGlyph: TOnDrawGlyph;
    FOnRelease: TNotifyEvent;
    procedure SetAllowAllUp(AValue: Boolean);
    procedure SetChecked(AValue: Boolean);
    procedure SetDelay(AValue: Integer);
    procedure SetGlyphColor(AValue: TColor);
    procedure SetGlyphDesign(AValue: TGlyphDesign);
    procedure SetGlyphDesignChecked(AValue: TGlyphDesign);
    procedure SetGroupIndex(AValue: Integer);
    procedure SetImageIndex(AValue: SmallInt);
    procedure SetImageIndexChecked(AValue: SmallInt);
    procedure SetImages(AValue: TCustomImageList);
    procedure SetLayout(AValue: TObjectPos);
    procedure SetMargin(AValue: SmallInt);
    procedure SetSpacing(AValue: SmallInt);
  protected const
    cDefHeight = 23;
    cDefSpacing = 6;
    cDefWidth = 21;
    cMargin: SmallInt = 6;  
  protected
    BtnBitmapPushedDisabled: TBitmap;
    BtnDrawnPushed: Boolean;
    BtnPushed: Boolean;
    NeedRedraw: Boolean;
    RealLayout: TObjectPos;
    Timer: TCustomTimer;
    ValidStates: TItemStates;
    procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: Integer; 
                                     {%H-}WithThemeSpace: Boolean); override;
    procedure Click; override;
    procedure CMBiDiModeChanged(var {%H-}Message: TLMessage); message CM_BIDIMODECHANGED;
    procedure CMButtonPressed(var Message: TLMessage); message CM_BUTTONPRESSED;
    procedure CMColorChanged(var {%H-}Message: TLMessage); message CM_COLORCHANGED;
    procedure CMParentColorChanged(var Message: TLMessage); message CM_PARENTCOLORCHANGED;
    procedure CreateValidBitmaps(AEnabled: Boolean);
    function DialogChar(var Message: TLMKey): boolean; override;
    procedure DrawButtonBMPs;
    procedure FontChanged(Sender: TObject); override;
    function GetActionLinkClass: TControlActionLinkClass; override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure MouseEnter; override;
    procedure MouseLeave; override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure Paint;  override;
    procedure Redraw;
    procedure ResizeInvalidate;
    procedure SetAction(Value: TBasicAction); override;
    procedure SetAutoSize(Value: Boolean); override;
    procedure SetParent(NewParent: TWinControl); override;
    procedure TextChanged; override;
    procedure TimerOnTimer(Sender: TObject);
    procedure UpdateGroup;
    procedure WMSize(var Message: TLMSize); message LM_SIZE;
    property CheckFromAction: Boolean read FCheckFromAction write FCheckFromAction;
  public
    BtnBitmaps: array [low(TItemState)..high(TItemState)] of TBitmap;
    UpdateCount: SmallInt;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure BeginUpdate;
    procedure EndUpdate;
    property AllowAllUp: Boolean read FAllowAllUp write SetAllowAllUp default False;
    property Checked: Boolean read FChecked write SetChecked default False;			
    property Delay: Integer read FDelay write SetDelay default 0;  { Delay = 0 - Button; Delay > 0 - Delayed Button; Delay < 0 - Toggle Button }    
    property GlyphColor: TColor read FGlyphColor write SetGlyphColor default clDefault;
    property GlyphDesign: TGlyphDesign read FGlyphDesign write SetGlyphDesign;  { set default in descendants }
    property GlyphDesignChecked: TGlyphDesign read FGlyphDesignChecked write SetGlyphDesignChecked default egdNone;
    property GroupIndex: Integer read FGroupIndex write SetGroupIndex default 0;
    property ImageIndex: SmallInt read FImageIndex write SetImageIndex default -1;
    property ImageIndexChecked: SmallInt read FImageIndexChecked write SetImageIndexChecked default -1;
    property Images: TCustomImageList read FImages write SetImages;
    property Layout: TObjectPos read FLayout write SetLayout default eopLeft;
    property Margin: SmallInt read FMargin write SetMargin default -1;
    property Spacing: SmallInt read FSpacing write SetSpacing default cDefSpacing;
    property OnDrawGlyph: TOnDrawGlyph read FOnDrawGlyph write FOnDrawGlyph;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
    property OnRelease: TNotifyEvent read FOnRelease write FOnRelease;
  end;

  { TECSpeedBtn }
  TECSpeedBtn = class(TCustomECSpeedBtn)
  published
    property Action;
    property Align;
    property AllowAllUp;
    property Anchors;
    property AutoSize;
    property BiDiMode;
    property BorderSpacing;
    property Caption;
    property Checked;
    {property Color;}  {does nothing ATM}
    property Constraints;
    property Delay;
    property Enabled;
    property Font;
    property GlyphColor;
    property GlyphDesign default egdNone;
    property GlyphDesignChecked;
    property GroupIndex;
    property ImageIndex;
    property ImageIndexChecked;
    property Images;
    property Layout;
    property Margin;
    property PopupMenu;
    property ParentBiDiMode;
    {property ParentColor;}  {does nothing ATM}
    property ParentFont;
    property ParentShowHint;
    property ShowHint;
    property Spacing;
    property Visible;
    property OnChangeBounds;
    property OnChange;
    property OnClick;
    property OnContextPopup;
    property OnDrawGlyph;
    property OnMouseDown;
    property OnMouseEnter;
    property OnMouseLeave;
    property OnMouseMove;
    property OnMouseUp;
    property OnRelease;
    property OnResize;    
  end;

  { TCustomECSpeedBtnPlus }    
  TCustomECSpeedBtnPlus = class(TCustomECSpeedBtn)
  protected
    CustomClick: TObjectMethod;
    CustomMouseDown: TMouseMethod;
    CustomMouseUp: TMouseMethod;
    CustomResize: TObjectMethod;
    procedure Click; override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; 
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure Resize; override;
  public 
    constructor Create(AOwner: TComponent); override;
  published
    property AnchorSideLeft stored False;
    property AnchorSideTop stored False;
    property AnchorSideRight stored False;
    property AnchorSideBottom stored False;     
    property Height stored False;
    property Left stored False;
    property Top stored False;
  end;
  
  { TECSpeedBtnPlus }
  TECSpeedBtnPlus = class(TCustomECSpeedBtnPlus)
  published
    property Caption;
    property GlyphColor;
    property GlyphDesign;
    property ImageIndex;
    property Images;
    property Layout;
    property Margin;
    property PopupMenu;
    property ParentShowHint;
    property ShowHint;
    property Spacing;
    property OnChangeBounds;
    property OnClick;
    property OnContextPopup;
    property OnMouseDown;
    property OnMouseEnter;
    property OnMouseLeave;
    property OnMouseMove;
    property OnMouseUp;
    property OnResize;  
  end;
  
  { TECEditBtnSpacing }
  TECEditBtnSpacing = class(TControlBorderSpacing)
  public
    function GetSpace(Kind: TAnchorKind): Integer; //override;
    procedure GetSpaceAround(var SpaceAround: TRect); //override;
  end;    
  
  { TBaseECEditBtn }
  TBaseECEditBtn = class(TCustomEdit)
  private
    FIndent: SmallInt;
    FOptions: TEBOptions;
    function GetWidthInclBtn: Integer;
    procedure SetIndent(AValue: SmallInt);
    procedure SetWidthInclBtn(AValue: Integer);
  protected
    FAnyButton: TCustomECSpeedBtnPlus;
    function ChildClassAllowed(ChildClass: TClass): boolean; override;
    procedure CMBiDiModeChanged(var Message: TLMessage); message CM_BIDIMODECHANGED;
    function CreateControlBorderSpacing: TControlBorderSpacing; override;
    procedure DoOnChangeBounds; override;
    procedure InitializeWnd; override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure SetButtonPosition;
    procedure SetEnabled(Value: Boolean); override;
    procedure SetParent(NewParent: TWinControl); override;
    procedure SetVisible(Value: Boolean); override;
  public
    constructor Create(AOwner: TComponent); override;
    procedure SetRealBoundRect(ARect: TRect);
    procedure SetRealBounds(ALeft, ATop, AWidth, AHeight: Integer);
    procedure SwitchOption(AOption: TEBOption; AOn: Boolean);
    property Indent: SmallInt read FIndent write SetIndent default 0;
    property Options: TEBOptions read FOptions write FOptions default cDefEBOptions;
    property WidthInclBtn: Integer read GetWidthInclBtn write SetWidthInclBtn stored False;
  end;

  { TECEditBtn }
  TECEditBtn = class(TBaseECEditBtn)
  private
    FButton: TECSpeedBtnPlus;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property Button: TECSpeedBtnPlus read FButton write FButton;
    property Indent;
    property Options;
    property WidthInclBtn;
  published  
    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 DragKind;
    property DragMode;
    property EchoMode;
    property Enabled;
    property Font;
    property HideSelection;  
    property MaxLength;
    property ParentBiDiMode;
    property ParentColor;
    property ParentFont;
    property ParentShowHint;
    property ReadOnly;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property Text;
    property Visible;      
    property OnChange;
    property OnChangeBounds;
    property OnClick;
    property OnContextPopup;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEditingDone;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMouseDown;
    property OnMouseEnter;
    property OnMouseLeave;
    property OnMouseMove;
    property OnMouseUp;
    property OnResize;
    property OnStartDrag;
    property OnUTF8KeyPress;   
  end;

const 
  cDefColorLayout = eclRGBColor;
  cDefCustomColor = clBlack;
    
type     
  { TECSpeedBtnColor }
  TECSpeedBtnColor = class(TCustomECSpeedBtnPlus)
  protected const
    cDefGlyphDesign = egdWinRectClr;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property Caption;
    property GlyphDesign default cDefGlyphDesign;
    property ImageIndex;
    property Images;
    property Layout;
    property Margin;
    property PopupMenu;
    property ParentShowHint;
    property ShowHint;
    property Spacing;
    property OnChangeBounds;
    property OnClick;
    property OnContextPopup;
    property OnMouseDown;
    property OnMouseEnter;
    property OnMouseLeave;
    property OnMouseMove;
    property OnMouseUp;
    property OnResize;               
  end;
  
  { TECColorBtn }
  TECColorBtn = class(TBaseECEditBtn)    
  private
    FButton: TECSpeedBtnColor;
    FColorLayout: TColorLayout;
    FCustomColor: TColor;
    FOnCustomColorChanged: TNotifyEvent;
    FPrefix: string;
    procedure SetColorLayout(AValue: TColorLayout);
    procedure SetCustomColor(AValue: TColor);
    procedure SetPrefix(AValue: string);
  protected
    procedure DoButtonClick;
    procedure Redraw;
  public
    constructor Create(AOwner: TComponent); override;
    procedure EditingDone; override;
  published
    property Button: TECSpeedBtnColor read FButton write FButton;
    property ColorLayout: TColorLayout read FColorLayout write SetColorLayout default cDefColorLayout;
    property CustomColor: TColor read FCustomColor write SetCustomColor default cDefCustomColor;
    property Indent;
    property Options;
    property Prefix: string read FPrefix write SetPrefix;
    property WidthInclBtn;
    property OnCustomColorChanged: TNotifyEvent read FOnCustomColorChanged write FOnCustomColorChanged;
  published
    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 DragKind;
    property DragMode;
    property Enabled;
    property Font;
    property HideSelection;
    property ParentBiDiMode;
    property ParentColor;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ReadOnly default True;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property Visible;
    property OnChange;
    property OnChangeBounds;
    property OnClick;
    property OnContextPopup;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEditingDone;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMouseDown;
    property OnMouseEnter;
    property OnMouseLeave;
    property OnMouseMove;
    property OnMouseUp;
    property OnResize;
    property OnStartDrag;
    property OnUTF8KeyPress;  
  end;

  { TECComboBtnSpacing }
  TECComboBtnSpacing = class(TControlBorderSpacing)
  public
    function GetSpace(Kind: TAnchorKind): Integer; //override;
    procedure GetSpaceAround(var SpaceAround: TRect); //override;
  end;       
  
  { TBaseECComboBtn }
  TBaseECComboBtn = class(TCustomComboBox)
  private
    FIndent: SmallInt;
    FItemOrder: TItemOrder;
    FMaxCount: Integer;
    FOptions: TEBOptions;
		function GetWidthInclBtn: Integer;
    procedure SetIndent(AValue: SmallInt);
    procedure SetItemOrder(AValue: TItemOrder);
    procedure SetMaxCount(AValue: Integer);
    procedure SetOptions(AValue: TEBOptions);
    procedure SetWidthInclBtn(AValue: Integer);
  protected 
    FAnyButton: TCustomECSpeedBtnPlus;
    function ChildClassAllowed(ChildClass: TClass): boolean; override;
    procedure CMBiDiModeChanged(var Message: TLMessage); message CM_BIDIMODECHANGED;
    function CreateControlBorderSpacing: TControlBorderSpacing; override;
    procedure DoOnChangeBounds; override;
    procedure InitializeWnd; override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure SetButtonPosition;
    procedure SetEnabled(Value: Boolean); override;
    procedure SetParent(NewParent: TWinControl); override;
    procedure SetSorted(Val: boolean); override;
    procedure SetVisible(Value: Boolean); override;
  public
    constructor Create(TheOwner: TComponent); override;
    procedure Add(AItem: string);
    procedure AddItemHistory(AItem: string; ACaseSensitive: Boolean);
    procedure AddItemLimit(AItem: string; ACaseSensitive: Boolean);
    procedure SetRealBoundRect(ARect: TRect);
    procedure SetRealBounds(ALeft, ATop, AWidth, AHeight: Integer);     
    property Indent: SmallInt read FIndent write SetIndent default 0;
    property ItemOrder: TItemOrder read FItemOrder write SetItemOrder; 
    property MaxCount: Integer read FMaxCount write SetMaxCount default 0;
    property Options: TEBOptions read FOptions write SetOptions default cDefEBOptions;
    property WidthInclBtn: Integer read GetWidthInclBtn write SetWidthInclBtn stored False;
  end;
  
  { TECComboBtn }      
  TECComboBtn = class(TBaseECComboBtn)
  private
    FButton: TECSpeedBtnPlus;
  protected const
    cDefItemOrder = eioFixed;
  public
    constructor Create(TheOwner: TComponent); override;
  published
    property Button: TECSpeedBtnPlus read FButton write FButton;
    property Indent;
    property ItemOrder default eioFixed;
    property Options;
    property WidthInclBtn;
  published
    property Align;
    property Anchors;
    property ArrowKeysTraverseList;
    property AutoComplete;
    property AutoCompleteText;
    property AutoDropDown;
    property AutoSelect;
    property AutoSize;
    property BidiMode;
    property BorderSpacing;
    property BorderStyle;
    property CharCase;
    property Color;
    property Constraints;
    property DragCursor;
    property DragKind;
    property DragMode;
    property DropDownCount;
    property Enabled;
    property Font;
    property ItemHeight;
    property ItemIndex;
    property Items;
    property ItemWidth;
    property MaxCount;
    property MaxLength;  
    property ParentBidiMode;
    property ParentColor;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ReadOnly;
    property ShowHint;
    property Style;
    property TabOrder;
    property TabStop;
    property Text;
    property Visible;  
    property OnChange;
    property OnChangeBounds;
    property OnClick;
    property OnCloseUp;
    property OnContextPopup;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnDrawItem;
    property OnEndDrag;
    property OnDropDown;
    property OnEditingDone;
    property OnEnter;
    property OnExit;
    property OnGetItems;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMeasureItem;
    property OnMouseDown;
    property OnMouseEnter;
    property OnMouseLeave;
    property OnMouseMove;
    property OnMouseUp;
    property OnStartDrag;
    property OnSelect;
    property OnUTF8KeyPress;  
  end;
    
  { TECColorCombo }
  TECColorCombo = class(TBaseECComboBtn)
  private
    FButton: TECSpeedBtnColor;
    FColorLayout: TColorLayout;
    FCustomColor: TColor;
    FPrefix: string;
    FOnCustomColorChanged: TNotifyEvent;
    procedure SetColorLayout(AValue: TColorLayout);
    procedure SetCustomColor(AValue: TColor);
    procedure SetPrefix(AValue: string);
  protected const
    cDefColorOrder = eioHistory;    
  protected
    FNeedMeasure: Boolean;
    FTextTop: SmallInt;
    FTextWidth: SmallInt;
    procedure DoButtonClick;
    procedure DrawItem(Index: Integer; ARect: TRect; State: TOwnerDrawState); override;
    procedure Select; override;  { only when ItemIndex changes by mouse }
    procedure SetItemIndex(const Val: Integer); override;  { only when ItemIndex changes by code }
    procedure SetItemHeight(const AValue: Integer); override;
  public
    constructor Create(AOwner: TComponent); override;     
    procedure AddColor(AColor: string); overload;
    procedure AddColor(AColor: TColor); overload;
    procedure Validate;
  published           				 
    property Button: TECSpeedBtnColor read FButton write FButton;
    property ColorLayout: TColorLayout read FColorLayout write SetColorLayout default cDefColorLayout;
    property CustomColor: TColor read FCustomColor write SetCustomColor default cDefCustomColor;
    property Indent;
    property ItemOrder default cDefColorOrder;
    property MaxCount;
    property Options;
    property Prefix: string read FPrefix write SetPrefix;
    property WidthInclBtn;
    property OnCustomColorChanged: TNotifyEvent read FOnCustomColorChanged write FOnCustomColorChanged;
  published
    property Align;
    property Anchors;
    property ArrowKeysTraverseList;
    property AutoDropDown;
    property AutoSize;
    property BidiMode;
    property BorderSpacing;
    property CharCase;
    property Color;
    property Constraints;
    property DragCursor;
    property DragKind;
    property DragMode;
    property DropDownCount;
    property Enabled;
    property Font;
    property ItemHeight;
    property ItemIndex;
    property Items;
    property ParentBidiMode;
    property ParentColor;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property Visible;  
    property OnChange;
    property OnChangeBounds;
    property OnClick;
    property OnCloseUp;
    property OnContextPopup;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDrag;
    property OnDropDown;
    property OnEditingDone;
    property OnEnter;
    property OnExit;
    property OnGetItems;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMouseDown;
    property OnMouseEnter;
    property OnMouseLeave;
    property OnMouseMove;
    property OnMouseUp;
    property OnStartDrag;
    property OnSelect;
    property OnUTF8KeyPress;  
  end;
  
procedure Register;

implementation

{ TECSpeedBtnActionLink }

procedure TECSpeedBtnActionLink.AssignClient(AClient: TObject);
begin
  inherited AssignClient(AClient);
  FClientSpeedBtn := AClient as TCustomECSpeedBtn;
end;

function TECSpeedBtnActionLink.IsCheckedLinked: Boolean;
begin
  Result := inherited IsCheckedLinked and
            (FClientSpeedBtn.Checked = (Action as TCustomAction).Checked);   
end;

procedure TECSpeedBtnActionLink.SetChecked(Value: Boolean);
begin
  if IsCheckedLinked then
    begin
      FClientSpeedBtn.CheckFromAction := True;
      try
        FClientSpeedBtn.Checked := Value;
      finally
        FClientSpeedBtn.CheckFromAction := False;
      end;
    end;    
end;

{ TCustomECSpeedBtn }

constructor TCustomECSpeedBtn.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ControlStyle := ControlStyle + [csNoFocus, csParentBackground, csReplicatable] 
                               - csMultiClicks - [csCaptureMouse, csOpaque, csSetCaption];   
  FGlyphColor := clDefault;
  FImageIndex := -1;
  FImageIndexChecked:=-1;
  FLayout := eopLeft;
  RealLayout:=eopLeft;
  FMargin := -1;
  FSpacing := cDefSpacing;
  SetInitialBounds(0, 0, cDefWidth, cDefHeight);
end;

destructor TCustomECSpeedBtn.Destroy;
var aState: TItemState;
begin
  for aState := low(TItemState) to high(TItemState) do
    FreeAndNil(BtnBitmaps[aState]);
  inherited Destroy;
end;

procedure TCustomECSpeedBtn.BeginUpdate;
begin
  inc(UpdateCount);
end;

procedure TCustomECSpeedBtn.CalculatePreferredSize(var PreferredWidth, 
            PreferredHeight: Integer; WithThemeSpace: Boolean);
var aCaption: string;
    aCaptionSize, aGlyphSize: TSize;
    aMargin: SmallInt;
begin
  aCaption := Caption;
  if aCaption <> '' then
    begin
      DeleteAmpersands(aCaption);
      aCaptionSize := Canvas.TextExtent(aCaption);
    end else
    aCaptionSize := Size(0, 0);
  if (ImageIndex >= 0) and assigned(Images) and (ImageIndex < Images.Count)
    then aGlyphSize := Size(Images.Width, Images.Height)
    else aGlyphSize := Canvas.GlyphExtent(GlyphDesign);     
  if RealLayout in [eopRight, eopLeft] then
    begin
      if aGlyphSize.cx*aCaptionSize.cx > 0 then inc(aGlyphSize.cx, Spacing);
      inc(aGlyphSize.cx, aCaptionSize.cx);
      if aCaptionSize.cx > 0 then inc(aGlyphSize.cx, 2*Spacing);
      aGlyphSize.cy := Math.max(aGlyphSize.cy, aCaptionSize.cy);
    end else
    begin
      aGlyphSize.cx := Math.max(aGlyphSize.cx, aCaptionSize.cx);
      if aGlyphSize.cy*aCaptionSize.cy > 0 then inc(aGlyphSize.cy, Spacing);
      inc(aGlyphSize.cy, aCaptionSize.cy);
    end;
  aMargin := Margin;
  if aMargin < 0 then aMargin := cMargin;
  inc(aGlyphSize.cx, 2*aMargin);
  inc(aGlyphSize.cy, 2*aMargin);
  aGlyphSize.cx := aGlyphSize.cx or 1;  { Odd size glyphs look better }
  aGlyphSize.cy := aGlyphSize.cy or 1;
  PreferredWidth := aGlyphSize.cx;
  PreferredHeight := aGlyphSize.cy;
end;

procedure TCustomECSpeedBtn.Click;
begin
  case Delay of
    low(Integer)..-1: if not assigned(Action) then Checked := not Checked;
    1..high(Integer): if not Checked then Checked := True
                        else
                        begin
                          Timer.Enabled := False;
                          Timer.Enabled := True;
                        end;
  end;
  inherited Click;
end;   

procedure TCustomECSpeedBtn.CMBiDiModeChanged(var Message: TLMessage);
var aRealLayout: TObjectPos;
begin
  aRealLayout := Layout;
  if IsRightToLeft then
    case aRealLayout of
      eopRight: aRealLayout := eopLeft;
      eopLeft: aRealLayout := eopRight;
    end;
  RealLayout := aRealLayout;
  Redraw;
end;   

procedure TCustomECSpeedBtn.CMButtonPressed(var Message: TLMessage);
var aSender: TCustomECSpeedBtn;
begin
  if csDestroying in ComponentState then exit;
  if Message.WParam = WParam(FGroupIndex) then
    begin
      aSender := TCustomECSpeedBtn(Message.LParam);
      if aSender <> self then
        begin
          if aSender.Checked and FChecked then
            begin
              FChecked := False;
              Invalidate;
            end;
          FAllowAllUp := aSender.AllowAllUp;
        end;
    end;         
end;   

procedure TCustomECSpeedBtn.CMColorChanged(var Message: TLMessage);
begin
  NeedRedraw := True;
end;   

procedure TCustomECSpeedBtn.CMParentColorChanged(var Message: TLMessage);
begin
  inherited CMParentColorChanged(Message);
  if not ParentColor then NeedRedraw := True;
end;   

procedure TCustomECSpeedBtn.CreateValidBitmaps(AEnabled: Boolean);
var aIState: TItemState;
    aWidth, aHeight: Integer;
begin
  for aIState in ValidStates do  
    FreeAndNil(BtnBitmaps[aIState]);
  if AEnabled 
    then ValidStates := caEnabledStates 
    else ValidStates := caDisabledStates;
  aWidth := Width;
  aHeight := Height;    
  for aIState in ValidStates do 
    begin
      BtnBitmaps[aIState] := TBitmap.Create;
      BtnBitmaps[aIState].SetProperties(aWidth, aHeight);
    end;          
end;                 
  
function TCustomECSpeedBtn.DialogChar(var Message: TLMKey): Boolean;
begin
  Result := False;
  if Message.Msg = LM_SYSCHAR then
    begin
      if IsEnabled and IsVisible then
        begin
          if IsAccel(Message.CharCode, Caption) then
            begin
              Click;
              Result := True;      
            end else
            Result := inherited DialogChar(Message);     
        end;
    end;
end;   

procedure TCustomECSpeedBtn.DrawButtonBMPs;
var aImageIndex, aImgIdxChckd, aLength: Integer;
    aCaption: string;
    aFlags: Cardinal;
    aGlyphDesign, aGlyphDsgnChckd: TGlyphDesign;
    aGlyphSize, aTextSize: TSize;
    aPoint: TPoint;
    aRect: TRect;
    aState: TItemState;
    
  procedure AdjustRect(var ARect: TRect; AWidthLimit, AHeightLimit: SmallInt);
  var i, j, aLimit: SmallInt;
  begin
    i := -4;
    aLimit := ARect.Right - ARect.Left;
    if aLimit <= 21 then inc(i);      
    aLimit := aLimit - AWidthLimit;
    if aLimit > 0 then dec(i, aLimit div 5);
    j := -4;                                   
    aLimit := ARect.Bottom - ARect.Top - AHeightLimit;
    if aLimit > 0 then dec(j, aLimit div 5);			
    InflateRect(ARect, i, j);
  end;            
    
begin
  {$IFDEF DBGCTRLS} DebugLn('TCustomECSpeedBtn.DrawButton'); {$ENDIF}
  if assigned(Parent) then
    begin
      aRect := ClientRect;
      for aState in ValidStates do
        begin
          BtnBitmaps[aState].TransparentColor := GetColorResolvingDefault(Color, Parent.Brush.Color);
          BtnBitmaps[aState].TransparentClear;
          BtnBitmaps[aState].Canvas.DrawButtonBackground(aRect, aState);
        end;
      if assigned(OnDrawGlyph) then 
        for aState in ValidStates do
          OnDrawGlyph(self, aState)
        else
        begin
          aCaption := Caption;
          DeleteAmpersands(aCaption);
          aTextSize := Canvas.TextExtent(aCaption);
          aImageIndex := ImageIndex;
          aImgIdxChckd := ImageIndexChecked;
          if assigned(Images) then
					  begin     
              aGlyphSize := Size(Images.Width, Images.Height);
              if (aImageIndex >= 0) and (aImageIndex < Images.Count) then  
                begin
                  if (aImgIdxChckd < 0) or (aImgIdxChckd >= Images.Count) then aImgIdxChckd := aImageIndex;
                end else
                if (aImgIdxChckd >= 0) and (aImgIdxChckd < Images.Count) 
                  then aImageIndex := aImgIdxChckd
                  else aImageIndex := -1;
            end else
            aImageIndex := -1;    
          if aImageIndex = -1 then
            begin
              aGlyphDesign := GlyphDesign;
              aGlyphDsgnChckd := GlyphDesignChecked;
              if aGlyphDsgnChckd = egdNone then aGlyphDsgnChckd := aGlyphDesign;
              if aGlyphDesign = egdNone then aGlyphDesign := GlyphDesignChecked;
              aGlyphSize := Canvas.GlyphExtent(aGlyphDesign);
              if aGlyphDesign > egdNone then
                begin
                  aGlyphSize.cx := aGlyphSize.cx or 1;
                  aGlyphSize.cy := aGlyphSize.cy or 1;
                end;             
            end;
          aFlags := DT_SingleLine;
          if aGlyphSize.cx*aTextSize.cx <> 0 then
            begin
              if RealLayout in [eopTop, eopBottom] then
                begin
                  aFlags := aFlags or DT_CENTER;
                  aLength := aGlyphSize.cy + aTextSize.cy;
                  aPoint.X := (Width - aGlyphSize.cx) div 2;
                end else 
                begin
                  aFlags := aFlags or DT_VCENTER;
                  aLength := aGlyphSize.cx + aTextSize.cx;
                  aPoint.Y := (Height - aGlyphSize.cy) div 2;
                end;
              inc(aLength, Spacing);
              case RealLayout of
                eopTop: 
                  begin
                    aPoint.Y := (Height - aLength) div 2;
                    aRect.Top := aPoint.Y + aGlyphSize.cy + Spacing;    
                  end;
                eopRight: 
                  begin
                    aRect.Left := (Width - aLength) div 2;
                    aPoint.X := aRect.Left + aTextSize.cx + Spacing;
                  end;
                eopBottom: 
                  begin
                    aRect.Top := (Height - aLength) div 2;
                    aPoint.Y := aRect.Top + aTextSize.cy + Spacing; 
                  end;
                eopLeft: 
                  begin
                    aPoint.X := (Width - aLength) div 2;
                    aRect.Left := aPoint.X + aGlyphSize.cx + Spacing;  
                  end;
              end; 
            end else
            begin
              aFlags := aFlags or DT_VCENTER or DT_CENTER;
              aPoint := Point((Width - aGlyphSize.cx) div 2, (Height - aGlyphSize.cy) div 2);  
            end;         
          if aTextSize.cx > 0 then
            begin              
              if UseRightToLeftReading then aFlags := aFlags or DT_RTLREADING;     
              for aState in ValidStates do
                begin
                  BtnBitmaps[aState].Canvas.Font.Assign(Font);
                  if BtnBitmaps[aState].Canvas.Font.Color = clDefault
                    then BtnBitmaps[aState].Canvas.Font.Color := clBtnText;  
                  with ThemeServices do 
                    DrawText(BtnBitmaps[aState].Canvas, GetElementDetails(caThemedContent[aState]), 
                      Caption, aRect, aFlags, 0);
                end;
            end;
          if aImageIndex >= 0 then
            begin
              if Delay = 0 then
                begin
                  for aState in ValidStates do
                    ThemeServices.DrawIcon(BtnBitmaps[aState].Canvas, 
                      ThemeServices.GetElementDetails(caThemedContent[aState]), 
                      aPoint, FImages, aImageIndex);
                end else
                begin
                  for aState in ValidStates*[eisDisabled, eisHighlighted, eisEnabled] do
                    ThemeServices.DrawIcon(BtnBitmaps[aState].Canvas, 
                      ThemeServices.GetElementDetails(caThemedContent[aState]), 
                      aPoint, FImages, aImageIndex);
                  for aState in ValidStates*[eisPushed, eisPushedHihlighted, eisPushedDisabled] do
                    ThemeServices.DrawIcon(BtnBitmaps[aState].Canvas, 
                      ThemeServices.GetElementDetails(caThemedContent[aState]), 
                      aPoint, FImages, aImgIdxChckd);
                end;
            end else
            begin
              if aTextSize.cx = 0 then 
                begin
                  case aGlyphDesign of
                    egdGrid..egdSizeArrLeft: AdjustRect(aRect, 30, 30);
                    egdRectBeveled..high(TGlyphDesign): AdjustRect(aRect, 85, 30);
                  end;
                end else
                aRect := Rect(aPoint.X, aPoint.Y, aPoint.X + aGlyphSize.cx, aPoint.Y + aGlyphSize.cy);
              if Delay = 0 then
                begin
                  if aGlyphDesign > egdNone then
                    for aState in ValidStates do
                      begin
                        BtnBitmaps[aState].Canvas.SetRealGlyphColor(GlyphColor, aState);
                        BtnBitmaps[aState].Canvas.DrawGlyph(aRect, aGlyphDesign);
                      end;
                end else
                begin
                  if aGlyphDesign > egdNone then    
                    for aState in ValidStates*[eisDisabled, eisHighlighted, eisEnabled] do
                      begin
                        BtnBitmaps[aState].Canvas.SetRealGlyphColor(GlyphColor, aState);
                        BtnBitmaps[aState].Canvas.DrawGlyph(aRect, aGlyphDesign) 
                      end;   
                  if aGlyphDsgnChckd > egdNone then         
                    for aState in ValidStates*[eisPushed, eisPushedHihlighted, eisPushedDisabled] do
                      begin
                        BtnBitmaps[aState].Canvas.SetRealGlyphColor(GlyphColor, aState);
                        BtnBitmaps[aState].Canvas.DrawGlyph(aRect, aGlyphDsgnChckd) 
                      end;
                end;
            end;
        end;
    end;
  NeedRedraw := False;
end;

procedure TCustomECSpeedBtn.EndUpdate;
begin
  dec(UpdateCount);
  if UpdateCount = 0 then
    begin
      NeedRedraw := True;
      if AutoSize then
        begin
          InvalidatePreferredSize;
          AdjustSize;
        end;
      Invalidate;
    end;
end;

procedure TCustomECSpeedBtn.FontChanged(Sender: TObject);
begin
  inherited FontChanged(Sender);
  NeedRedraw := True;  { Invalidate not necessary here }  
end;

function TCustomECSpeedBtn.GetActionLinkClass: TControlActionLinkClass;
begin
  Result := TECSpeedBtnActionLink;
end;

procedure TCustomECSpeedBtn.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited MouseDown(Button, Shift, X, Y);
  if Button = mbLeft then BtnPushed := True; 
  if not Checked then Invalidate;
end;

procedure TCustomECSpeedBtn.MouseLeave;
begin
  {$IFDEF DBGCTRLS} DebugLn('TBaseECSpeedBtn.MouseLeave'); {$ENDIF}
  inherited MouseLeave;
  if BtnPushed then MouseUp(mbLeft, [ssLeft], 0, 0);
  Invalidate;  
end;

procedure TCustomECSpeedBtn.MouseEnter;
begin
  inherited MouseEnter;
  Invalidate;  
end;

procedure TCustomECSpeedBtn.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited MouseUp(Button, Shift, X, Y);      
  BtnPushed := False;                    
  if Delay = 0 then Invalidate;  { when Delay <> 0 SetChecked cares of Invalidate; }
end;

procedure TCustomECSpeedBtn.Paint;
var aState: TItemState;
    bEnabled: Boolean;
begin
  {$IFDEF DBGCTRLS} DebugLn('TCustomECSpeedBtn.Paint'); {$ENDIF}
  inherited Paint;
  bEnabled := IsEnabled;
  if (bEnabled xor (eisEnabled in ValidStates)) or (ValidStates=[]) then
    begin
      NeedRedraw := True;
      CreateValidBitmaps(bEnabled);
    end;    
  if NeedRedraw then DrawButtonBMPs;
  aState := eisEnabled;
  if bEnabled then
    begin
      if BtnPushed or Checked or (assigned(Timer) and Timer.Enabled) then
        begin
          if not MouseEntered 
            then aState := eisPushed
            else aState := eisPushedHihlighted;
        end else 
        if MouseEntered then aState := eisHighlighted;    
    end else
    begin
      if Checked 
        then aState := eisPushedDisabled
        else aState := eisDisabled;
    end;    
  Canvas.Draw(0, 0, BtnBitmaps[aState]);
  BtnDrawnPushed := (aState in [eisPushed, eisPushedHihlighted]);
end;

procedure TCustomECSpeedBtn.Redraw;
begin
  NeedRedraw := True;
  if UpdateCount = 0 then Invalidate;
end;

procedure TCustomECSpeedBtn.ResizeInvalidate;
begin
  if UpdateCount = 0 then
    begin;
      if AutoSize then
        begin
          InvalidatePreferredSize;
          AdjustSize;
        end;    
      Invalidate;
    end;
end;            

procedure TCustomECSpeedBtn.SetAction(Value: TBasicAction);
begin
  inherited SetAction(Value);
  if assigned(Value) and (Value is TCustomAction) and
    (Value as TCustomAction).AutoCheck then Delay := -1;
end;            

procedure TCustomECSpeedBtn.SetAutoSize(Value: Boolean);
begin
  inherited SetAutoSize(Value);
  if Value then
    begin
      InvalidatePreferredSize;
      AdjustSize;
      NeedRedraw := True;
      Invalidate;   
    end; 
end;                        

procedure TCustomECSpeedBtn.SetParent(NewParent: TWinControl);
begin
  inherited SetParent(NewParent);
  NeedRedraw := True;
end;  

procedure TCustomECSpeedBtn.TextChanged;
begin
  inherited TextChanged;
  NeedRedraw := True;
  ResizeInvalidate; 
end;             

procedure TCustomECSpeedBtn.TimerOnTimer(Sender: TObject);
begin
  Timer.Enabled := False;
  Checked := False;
  Invalidate;
end;          

procedure TCustomECSpeedBtn.UpdateGroup;
var aMsg : TLMessage;
begin
  if (FGroupIndex <> 0) and (Parent <> nil) and (not (csLoading in ComponentState)) then
  begin
    aMsg.Msg := CM_ButtonPressed;
    aMsg.WParam := FGroupIndex;
    aMsg.LParam := PtrInt(self);
    aMsg.Result := 0;
    Parent.Broadcast(aMsg);
  end;    
end;          

procedure TCustomECSpeedBtn.WMSize(var Message: TLMSize);
var aState: TItemState;
begin
  inherited WMSize(Message);
  for aState in ValidStates do
    BtnBitmaps[aState].SetSize(Message.Width, Message.Height);
  Redraw;
end;          

{ TCustomECSpeedBtn.Setters }

procedure TCustomECSpeedBtn.SetAllowAllUp(AValue: Boolean);
begin
  if FAllowAllUp = AValue then exit;
  FAllowAllUp := AValue;
  UpdateGroup;
end; 

procedure TCustomECSpeedBtn.SetChecked(AValue: Boolean);
begin                                                  
  if (FChecked = AValue) or (not AValue and (GroupIndex <> 0) and (not AllowAllUp)) then exit;
  FChecked := AValue;
  if [csLoading, csDestroying, csDesigning]*ComponentState = [] then
    begin
      if GroupIndex <> 0 then UpdateGroup;
      if AValue then 
        begin
          if Delay = 0 then Delay := -1;    
          if Delay > 0 then Timer.Enabled := True;
        end;
      if assigned(OnChange) then OnChange(self);
      if not AValue and assigned(OnRelease) then OnRelease(self);
    end; 
  if not AValue or not BtnDrawnPushed then Invalidate;
end;     

procedure TCustomECSpeedBtn.SetDelay(AValue: Integer);
begin
  if FDelay = AValue then exit;       
  if ((AValue = 0) or (FDelay = 0)) and ((ImageIndex <> ImageIndexChecked) 
    or (GlyphDesign <> GlyphDesignChecked)) then NeedRedraw := True;
  FDelay := AValue;
  if AValue >= 0 then
    begin
      FGroupIndex := 0;
      Checked := False; 
    end;
  if AValue > 0 then
    begin
      if not assigned(Timer) then
        begin
          Timer := TCustomTimer.Create(self);
          with Timer do
            begin
              Enabled := False;   
              OnTimer := @TimerOnTimer;
            end;
        end;
      Timer.Interval := AValue;
    end else
    if assigned(Timer) then FreeAndNil(Timer);
end;    

procedure TCustomECSpeedBtn.SetGlyphColor(AValue: TColor);
begin
  if FGlyphColor = AValue then exit;
  FGlyphColor := AValue;
  Redraw;
end;

procedure TCustomECSpeedBtn.SetGlyphDesign(AValue: TGlyphDesign);
begin
  if FGlyphDesign=AValue then exit;
  FGlyphDesign:=AValue;
  NeedRedraw := True;
  ResizeInvalidate;
end;   

procedure TCustomECSpeedBtn.SetGlyphDesignChecked(AValue: TGlyphDesign);
begin
  if FGlyphDesignChecked = AValue then exit;
  FGlyphDesignChecked := AValue;
  Redraw;
end;   

procedure TCustomECSpeedBtn.SetGroupIndex(AValue: Integer);
begin
  if FGroupIndex = AValue then exit;
  FGroupIndex := AValue;
  if AValue <> 0 then 
    begin
      FDelay := -1;  { only checkable button makes sense in group }
      UpdateGroup;
    end;
end;   

procedure TCustomECSpeedBtn.SetImageIndex(AValue: SmallInt);
begin
  if FImageIndex = AValue then exit;
  FImageIndex := AValue;
  NeedRedraw := True;
  ResizeInvalidate;
end;

procedure TCustomECSpeedBtn.SetImageIndexChecked(AValue: SmallInt);
begin
  if FImageIndexChecked = AValue then exit;
  FImageIndexChecked := AValue;
  Redraw;
end;

procedure TCustomECSpeedBtn.SetImages(AValue: TCustomImageList);
begin
  if FImages = AValue then exit; 
  FImages := AValue;
  NeedRedraw := True;
  ResizeInvalidate;
end;

procedure TCustomECSpeedBtn.SetLayout(AValue: TObjectPos);
begin
  if FLayout = AValue then exit;
  FLayout := AValue;
  if IsRightToLeft then
    case AValue of
      eopRight: AValue := eopLeft;
      eopLeft: AValue := eopRight;
    end;
  RealLayout := AValue;                           
  NeedRedraw := True;
  ResizeInvalidate;
end;

procedure TCustomECSpeedBtn.SetMargin(AValue: SmallInt);
begin
  if FMargin = AValue then exit;
  FMargin := AValue;    
  NeedRedraw := True;
  ResizeInvalidate;
end;

procedure TCustomECSpeedBtn.SetSpacing(AValue: SmallInt);
begin
  if FSpacing = AValue then exit;
  FSpacing := AValue;
  NeedRedraw := True;
  ResizeInvalidate;  
end;

{ TCustomECSpeedBtnPlus }

constructor TCustomECSpeedBtnPlus.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ControlStyle := ControlStyle + [csNoDesignSelectable];
  SetSubComponent(True);
end;

procedure TCustomECSpeedBtnPlus.Click;
begin
  inherited Click;
  if assigned(CustomClick) then CustomClick;
end;     

procedure TCustomECSpeedBtnPlus.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited MouseDown(Button, Shift, X, Y);
  if assigned(CustomMouseDown) then CustomMouseDown(Button, Shift);
  if assigned(Owner) and (Owner is TWinControl) and (Owner as TWinControl).CanFocus 
    then (Owner as TWinControl).SetFocus;   
end;    

procedure TCustomECSpeedBtnPlus.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited MouseUp(Button, Shift, X, Y);
  if assigned(CustomMouseUp) then CustomMouseUp(Button, Shift);
end;    

procedure TCustomECSpeedBtnPlus.Resize;
begin
  inherited Resize;
  if assigned(CustomResize) then CustomResize;
end;    

{ TECEditBtnSpacing }

function TECEditBtnSpacing.GetSpace(Kind: TAnchorKind): Integer;
begin
  Result:=inherited GetSpace(Kind);
  case Kind of
    akLeft: if Control.IsRightToLeft then inc(Result, TBaseECEditBtn(Control).FAnyButton.Width);   
    akRight: if not Control.IsRightToLeft then inc(Result, TBaseECEditBtn(Control).FAnyButton.Width);
  end;              
end;

procedure TECEditBtnSpacing.GetSpaceAround(var SpaceAround: TRect);
begin
  inherited GetSpaceAround(SpaceAround);
  if not Control.IsRightToLeft 
    then inc(SpaceAround.Right, TBaseECEditBtn(Control).FAnyButton.Width)
    else inc(SpaceAround.Left, TBaseECEditBtn(Control).FAnyButton.Width);             
end;
    
{ TBaseECEditBtn }

constructor TBaseECEditBtn.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ControlStyle := ControlStyle - [csSetCaption];
  with FAnyButton do
    begin
      AnchorParallel(akTop, 0, self);
      AnchorParallel(akBottom, 0, self);   
      SetButtonPosition;
      CustomResize := @SetButtonPosition;
    end;
  FOptions := cDefEBOptions;
end;

function TBaseECEditBtn.ChildClassAllowed(ChildClass: TClass): boolean;
begin
  Result := (ChildClass = TCustomECSpeedBtnPlus);
end;

procedure TBaseECEditBtn.CMBiDiModeChanged(var Message: TLMessage);
begin
  inherited CMBiDiModeChanged(Message);
  SetButtonPosition;
end;

function TBaseECEditBtn.CreateControlBorderSpacing: TControlBorderSpacing;
begin
  Result := TECEditBtnSpacing.Create(self);
end;

procedure TBaseECEditBtn.DoOnChangeBounds;
begin
  inherited DoOnChangeBounds;
  SetButtonPosition;
end;

procedure TBaseECEditBtn.InitializeWnd;
begin
  inherited InitializeWnd;
  SetButtonPosition;
end; 
      
procedure TBaseECEditBtn.KeyDown(var Key: Word; Shift: TShiftState);
begin
  case Key of
    VK_RETURN: 
      if ((ssModifier in Shift) and (eboClickCtrlEnter in FOptions)) or 
        ((ssAlt in Shift) and (eboClickAltEnter in FOptions)) or
        ((ssShift in Shift) and (eboClickShiftEnter in FOptions)) 
        then FAnyButton.Click;
    VK_SPACE: 
      if (ssModifier in Shift) or ReadOnly then FAnyButton.Click;
  end;
  inherited KeyDown(Key, Shift);
end;

procedure TBaseECEditBtn.SetButtonPosition;
begin
  if not IsRightToLeft
    then FAnyButton.Left:=Left+Width+Indent
    else FAnyButton.Left:=Left-Indent-FAnyButton.Width;         
end;    

procedure TBaseECEditBtn.SetEnabled(Value: Boolean);
begin
  inherited SetEnabled(Value);        
  FAnyButton.Enabled := Value;
end;    

procedure TBaseECEditBtn.SetParent(NewParent: TWinControl);
begin
  inherited SetParent(NewParent);
  FAnyButton.Parent := Parent;
end;

procedure TBaseECEditBtn.SetRealBoundRect(ARect: TRect);
begin
  if BiDiMode = bdLeftToRight 
    then dec(ARect.Right, Indent + FAnyButton.Width)
    else inc(ARect.Left, Indent + FAnyButton.Width);
  BoundsRect := ARect;
end; 
          
procedure TBaseECEditBtn.SetRealBounds(ALeft, ATop, AWidth, AHeight: Integer);
begin
  if BiDiMode <> bdLeftToRight then ALeft := ALeft + Indent + FAnyButton.Width;
  SetBounds(ALeft, ATop, AWidth - Indent - FAnyButton.Width, AHeight);
end;                  

procedure TBaseECEditBtn.SetVisible(Value: Boolean);
begin
  inherited SetVisible(Value);
  FAnyButton.Visible := Value;
end;     

procedure TBaseECEditBtn.SwitchOption(AOption: TEBOption; AOn: Boolean);
var aOptions: TEBOptions;
begin
  aOptions := FOptions;
  if AOn 
    then Include(aOptions, AOption)
    else Exclude(aOptions, AOption);
  Options := aOptions;
end;       

{ Setters }

function TBaseECEditBtn.GetWidthInclBtn: Integer;
begin
  Result := Width + Indent + FAnyButton.Width;
end;     

procedure TBaseECEditBtn.SetIndent(AValue: SmallInt);
begin
  if FIndent = AValue then exit;
  FIndent := AValue;
  SetButtonPosition;
end;

procedure TBaseECEditBtn.SetWidthInclBtn(AValue: Integer);
begin
  Width := AValue - Indent - FAnyButton.Width;
end;

{ TECEditBtn }

constructor TECEditBtn.Create(AOwner: TComponent);
begin
  FButton := TECSpeedBtnPlus.Create(self);
  FAnyButton := FButton;
  FButton.Name := 'ECEditBtnButton';
  inherited Create(AOwner);    
end;   

{ TECSpeedBtnColor }

constructor TECSpeedBtnColor.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FGlyphDesign := cDefGlyphDesign;
end;        

{ TECColorBtn }

constructor TECColorBtn.Create(AOwner: TComponent);
begin
  FButton := TECSpeedBtnColor.Create(self);
  FAnyButton := FButton;
  with FButton do
    begin  
      CustomClick := @DoButtonClick;
      Name := 'ECCBSpeedBtn';
      Width := 27;
    end;     
  inherited Create(AOwner);
  ReadOnly := True;
  FCustomColor := cDefCustomColor;
  FPrefix := '$';
  Redraw;
end;

procedure TECColorBtn.DoButtonClick;
var aAlpha: Integer;
    aColorDialog: TColorDialog;
begin
  aAlpha := CustomColor and $FF000000;
  aColorDialog := TColorDialog.Create(self);
  aColorDialog.Color := CustomColor and $FFFFFF;
  if aColorDialog.Execute then CustomColor := aAlpha + aColorDialog.Color;
  aColorDialog.Free;
  SetFocus;
end;        

procedure TECColorBtn.EditingDone;
var aColor: TColor;
begin   
  inherited EditingDone;
  if TryStrToColorLayouted(Text, ColorLayout, aColor) then CustomColor := aColor; 
end;

procedure TECColorBtn.Redraw;
var aPrefix: string;
begin
  if ColorLayout <> eclSystemBGR 
    then aPrefix := Prefix
    else aPrefix := '';
  Text := aPrefix + ColorToStrLayouted(CustomColor, ColorLayout);
end;

{ Setters }

procedure TECColorBtn.SetCustomColor(AValue: TColor);
begin
  if FCustomColor = AValue then exit;
  FCustomColor := AValue;
  FButton.GlyphColor := AValue and $FFFFFF;
  Redraw;
  if assigned(OnCustomColorChanged) then OnCustomColorChanged(self);
end;

procedure TECColorBtn.SetColorLayout(AValue: TColorLayout);
begin
  if FColorLayout = AValue then exit;
  FColorLayout := AValue;
  Redraw;
end;

procedure TECColorBtn.SetPrefix(AValue: string);
begin
  if FPrefix = AValue then exit;
  FPrefix := AValue;
  Redraw;
end;

{ TECComboBtnSpacing }

function TECComboBtnSpacing.GetSpace(Kind: TAnchorKind): Integer;
begin
  Result:=inherited GetSpace(Kind);
  case Kind of
    akLeft: if Control.IsRightToLeft then inc(Result, TBaseECComboBtn(Control).FAnyButton.Width);   
    akRight: if not Control.IsRightToLeft then inc(Result, TBaseECComboBtn(Control).FAnyButton.Width);
  end;  
end;

procedure TECComboBtnSpacing.GetSpaceAround(var SpaceAround: TRect);
begin
  inherited GetSpaceAround(SpaceAround);
  if not Control.IsRightToLeft 
    then inc(SpaceAround.Right, TBaseECComboBtn(Control).FAnyButton.Width)
    else inc(SpaceAround.Left, TBaseECComboBtn(Control).FAnyButton.Width);         
end;   

{ TBaseECComboBtn }

constructor TBaseECComboBtn.Create(TheOwner: TComponent);
begin
  inherited Create(TheOwner);
  ControlStyle := ControlStyle - [csSetCaption];
  with FAnyButton do
    begin
      AnchorParallel(akTop, 0, self);
      AnchorParallel(akBottom, 0, self);   
      SetButtonPosition;
      CustomResize := @SetButtonPosition;
    end;
  FOptions := cDefEBOptions;
end;

procedure TBaseECComboBtn.Add(AItem: string);
begin
  case ItemOrder of
    eioFixed: AddItemLimit(AItem, False);
    eioHistory: AddItemHistory(AItem, False);
    eioSorted: AddItemLimit(AItem, False);
  end;
end;

procedure TBaseECComboBtn.AddItemHistory(AItem: string; ACaseSensitive: Boolean);
var aMaxCount: Integer;
begin
  aMaxCount := MaxCount;
  if aMaxCount <= 0 then aMaxCount := high(Integer);
  AddHistoryItem(aItem, aMaxCount, True, ACaseSensitive);
end;

procedure TBaseECComboBtn.AddItemLimit(AItem: string; ACaseSensitive: Boolean);
var i, aCount: Integer;
    aDuplicates: TDuplicates;
begin
  aDuplicates := TStringList(Items).Duplicates;
  Items.BeginUpdate;
  if (aDuplicates <> dupAccept) and not TStringList(Items).Sorted then    
    begin
      if not ACaseSensitive then
        begin
          for i := Items.Count -1 downto 0 do
            if AnsiCompareText(Items[i], AItem) = 0 then 
              case aDuplicates of
                dupIgnore: Items.Delete(i);
                dupError: 
                  begin
                    Items.EndUpdate;
                    Exit;  { Exit! }
                  end;
              end;
        end else
        begin
          for i := Items.Count -1 downto 0 do
            if Items[i] = AItem then 
              case aDuplicates of
                dupIgnore: Items.Delete(i);
                dupError: 
                  begin
                    Items.EndUpdate;
                    Exit;  { Exit! }
                  end;
              end;               
        end;
    end;
  if not TStringList(Items).Sorted 
    then Items.Insert(Items.Count, AItem)  { add new item to the end }
    else Items.Add(AItem);                 { or somewhere ...}
  { remove overflow item from the beginning; it works on sorted list too, so beware }
  aCount := MaxCount;
  if aCount <= 0 then aCount := high(Integer);    
  for i := 1 to Items.Count - aCount do
    Items.Delete(0);
  Items.EndUpdate;
end;

function TBaseECComboBtn.ChildClassAllowed(ChildClass: TClass): boolean;
begin
  Result := (ChildClass = TCustomECSpeedBtnPlus);
end;

procedure TBaseECComboBtn.CMBiDiModeChanged(var Message: TLMessage);
begin
  inherited CMBiDiModeChanged(Message);
  SetButtonPosition;
end;  

function TBaseECComboBtn.CreateControlBorderSpacing: TControlBorderSpacing;
begin
  Result := TECComboBtnSpacing.Create(self);
end;

procedure TBaseECComboBtn.DoOnChangeBounds;
begin
  inherited DoOnChangeBounds;
  SetButtonPosition;
end;

procedure TBaseECComboBtn.InitializeWnd;
begin
  inherited InitializeWnd;
  SetButtonPosition;
end;

procedure TBaseECComboBtn.KeyDown(var Key: Word; Shift: TShiftState);
begin
  case Key of 
    VK_RETURN:
      begin
        if ((ssModifier in Shift) and (eboClickCtrlEnter in Options)) or
          ((ssAlt in Shift) and (eboClickAltEnter in Options)) or
          ((ssShift in Shift) and (eboClickShiftEnter in Options)) 
          then FAnyButton.Click;
      end;
    VK_SPACE:
      if (ssModifier in Shift) or ReadOnly then FAnyButton.Click;
  end;
  inherited KeyDown(Key, Shift);
end;

procedure TBaseECComboBtn.SetButtonPosition;
begin
  if not IsRightToLeft
    then FAnyButton.Left:=Left+Width+Indent
    else FAnyButton.Left:=Left-Indent-FAnyButton.Width;         
end;

procedure TBaseECComboBtn.SetEnabled(Value: Boolean);
begin
  inherited SetEnabled(Value); 
  FAnyButton.Enabled := Value;
end;

procedure TBaseECComboBtn.SetParent(NewParent: TWinControl);
begin
  inherited SetParent(NewParent);
  FAnyButton.Parent := NewParent;
  if assigned(NewParent) then SetButtonPosition;
end;        

procedure TBaseECComboBtn.SetRealBoundRect(ARect: TRect);
begin
  if BiDiMode = bdLeftToRight 
    then dec(ARect.Right, Indent + FAnyButton.Width)
    else inc(ARect.Left, Indent + FAnyButton.Width);
  BoundsRect := ARect;    
end;

procedure TBaseECComboBtn.SetRealBounds(ALeft, ATop, AWidth, AHeight: Integer);
begin
  if BiDiMode <> bdLeftToRight then ALeft := ALeft + Indent + FAnyButton.Width;
  SetBounds(ALeft, ATop, AWidth - Indent - FAnyButton.Width, AHeight);  
end;        

procedure TBaseECComboBtn.SetSorted(Val: boolean);
begin
  if Val then FItemOrder := eioSorted;
  inherited SetSorted(Val);
end;         

procedure TBaseECComboBtn.SetVisible(Value: Boolean);
begin
  inherited SetVisible(Value);
  FAnyButton.Visible := Value;
end;        

{ TBaseECComboBtn.Setters }

function TBaseECComboBtn.GetWidthInclBtn: Integer;
begin
  Result := Width + Indent + FAnyButton.Width;
end;       

procedure TBaseECComboBtn.SetIndent(AValue: SmallInt);
begin
  if FIndent = AValue then exit;
  FIndent := AValue;
  SetButtonPosition;
end;

procedure TBaseECComboBtn.SetItemOrder(AValue: TItemOrder);
begin
  if FItemOrder = AValue then exit;
  FItemOrder := AValue;
  Sorted := (AValue = eioSorted);
end;         

procedure TBaseECComboBtn.SetMaxCount(AValue: Integer);
var i: Integer;
begin
  if FMaxCount=AValue then exit;
  FMaxCount:=AValue;
  if (AValue > 0) and (AValue < Items.Count) then 
    begin
      Items.BeginUpdate;
      for i := Items.Count - 1 downto AValue do 
        Items.Delete(i);
      Items.EndUpdate;
    end;
end;     

procedure TBaseECComboBtn.SetOptions(AValue: TEBOptions);
begin
  if FOptions = AValue then exit;
  FOptions := AValue;
end;

procedure TBaseECComboBtn.SetWidthInclBtn(AValue: Integer);
begin
  Width := AValue - Indent - FAnyButton.Width;
end;

{ TECComboBtn }

constructor TECComboBtn.Create(TheOwner: TComponent);
begin
  FButton := TECSpeedBtnPlus.Create(self);
  FAnyButton := FButton;
  FButton.Name := 'ECCSpeedBtn'; 
  FItemOrder := cDefItemOrder;
  inherited Create(TheOwner); 
end;
           
{ TECColorCombo }

constructor TECColorCombo.Create(AOwner: TComponent);
begin
  FButton := TECSpeedBtnColor.Create(self);
  FAnyButton := FButton;
  with FButton do
    begin  
      CustomClick := @DoButtonClick;
      Name := 'ECCCSpeedBtn';
      Width := 27;
    end;  
  inherited Create(AOwner);
  TStringList(Items).Duplicates := dupIgnore;
  ReadOnly := True;
  FItemOrder := cDefColorOrder;
  FCustomColor := cDefCustomColor;
  FPrefix := '$';
  Style := csOwnerDrawFixed;
  FNeedMeasure := True;
end;         

procedure TECColorCombo.AddColor(AColor: string);
var anyColor: TColor;
begin
  if TryStrToColorLayouted(AColor, ColorLayout, anyColor) then AddColor(anyColor);       
end;        

procedure TECColorCombo.AddColor(AColor: TColor);
var aPrefix: string;
begin
  if ColorLayout <> eclSystemBGR
    then aPrefix := Prefix
    else aPrefix := '';      
  case ItemOrder of
    eioFixed: AddItemLimit(aPrefix + ColorToStrLayouted(AColor, ColorLayout), False);
    eioHistory: 
      begin
        AddItemHistory(aPrefix + ColorToStrLayouted(AColor, ColorLayout), False);
        ItemIndex := 0;  
      end;
    eioSorted: Items.Add(aPrefix + ColorToStrLayouted(AColor, ColorLayout)); 
  end;      
end;             

procedure TECColorCombo.DoButtonClick;
var aAlpha: Integer;
    aColorDialog: TColorDialog;
begin
  aAlpha := CustomColor and $FF000000;
  aColorDialog := TColorDialog.Create(self);
  aColorDialog.Color := CustomColor and $FFFFFF;
  if aColorDialog.Execute then CustomColor := aAlpha + aColorDialog.Color;
  aColorDialog.Free;
  SetFocus;         
end;         

procedure TECColorCombo.DrawItem(Index: Integer; ARect: TRect; State: TOwnerDrawState);
var aColor: TColor;
    aExtent: TSize;
begin  { do not call inherited ! }  
  {$IFDEF DBGCTRLS} DebugLn('DrawItem', ColorToString(Canvas.Brush.Color)); {$ENDIF}
  if not (odSelected in State) then Canvas.Brush.Color := clWindow;
  if (Index <> 0) or (ARect.Top = 0) then Canvas.FillRect(ARect);                                          
  if FNeedMeasure then 
    begin
      if ColorLayout in [eclRGBColor, eclBGRColor, eclCMYColor, eclYMCColor]
        then aExtent := Canvas.TextExtent(Prefix + 'F9CDEB')
        else aExtent := Canvas.TextExtent(Prefix + 'F9CDEBA8');
      FTextTop := (ARect.Bottom - ARect.Top - aExtent.cy) div 2;;
      FTextWidth := aExtent.cx;
      FNeedMeasure := False;    
    end;
  Canvas.Brush.Style := bsClear;
  if not (odSelected in State) or ((Index = 0) and (ARect.Top <> 0))
    then Canvas.Font.Color := clWindowText
    else Canvas.Font.Color := clHighlightText;
  Canvas.TextOut(ARect.Left + 3, ARect.Top + FTextTop, Items[Index]);
  if TryStrToColorLayouted(Items[Index], ColorLayout, aColor) then 
    with Canvas do
      begin
        Brush.Style := bsSolid;
        Brush.Color := aColor;
        Rectangle(ARect.Left + FTextWidth + 5, ARect.Top + 1, ARect.Right - 3, ARect.Bottom - 1);
        if (Index = 0) and (Items.Count <= 1) then CustomColor := aColor;       
      end;
end;           

procedure TECColorCombo.Validate;
var aColor: TColor;
begin
  if TryStrToColorLayouted(Text, ColorLayout, aColor) then CustomColor := aColor;
end;           

procedure TECColorCombo.Select;  { only when ItemIndex changes by mouse }
var aColor: TColor;
begin
  {$IFDEF DBGCTRLS} DebugLn('Select ', inttostr(ItemIndex));  {$ENDIF}
  if ItemOrder = eioHistory then
    begin
      Items.Move(ItemIndex, 0);
      ItemIndex := 0;
    end;
  inherited Select;
  if (ItemOrder <> eioHistory) and
    TryStrToColorLayouted(Items[ItemIndex], ColorLayout, aColor) then CustomColor := aColor;
end;

procedure TECColorCombo.SetItemIndex(const Val: Integer);  { only when ItemIndex changes by code }
var aColor: TColor;
    aLoading: Boolean;
    aText: string;
begin
  {$IFDEF DBGCTRLS} DebugLn('TECColorCombo.SetItemIndex'); {$ENDIF}
  aLoading := (csLoading in ComponentState);
  if not aLoading and (ItemOrder = eioHistory) then
    begin
      if (Val > 0) and (Val < Items.Count) then Items.Move(Val, 0);
      inherited SetItemIndex(0);
    end else
    inherited SetItemIndex(Val);
  if not aLoading then
    begin
      if Val >= 0
        then aText := Items[Val]
        else aText := Text;
      if TryStrToColorLayouted(aText, ColorLayout, aColor) then CustomColor := aColor;
    end;
end;

{ TECColorCombo.Setters }

procedure TECColorCombo.SetColorLayout(AValue: TColorLayout);
var aColor: TColor;
    i: Integer;
    aOldLayout: TColorLayout;
    aPrefix: string;					
begin                        
  if FColorLayout = AValue then exit;
  aOldLayout := FColorLayout;
  FColorLayout := AValue;
  FNeedMeasure := True;
  if not (AValue = eclSystemBGR) 
    then aPrefix := Prefix
    else aPrefix := '';
  for i := 0 to Items.Count - 1 do
    if TryStrToColorLayouted(Items[i], aOldLayout, aColor) then
      Items[i] := aPrefix + ColorToStrLayouted(aColor, AValue);
end;

procedure TECColorCombo.SetCustomColor(AValue: TColor);
var aColorString: string;
    aIndex: Integer;
begin
  {$IFDEF DBGCTRLS} DebugLn('SetCustomColor ', ColorToString(AValue)); {$ENDIF}
  if FCustomColor = AValue then exit;
  FCustomColor := AValue;
  FButton.GlyphColor := AValue and $FFFFFF;
  if not (csLoading in ComponentState) then
    begin
      aColorString := ColorToStrLayouted(AValue, ColorLayout);
      if ColorLayout <> eclSystemBGR then aColorString := Prefix + aColorString;
      aIndex := Items.IndexOf(aColorString);
      case ItemOrder of
        eioFixed: 
          begin
            if aIndex = -1 then
              begin
                aIndex := Items.Add(aColorString);
                ItemIndex := aIndex;
              end else 
              ItemIndex := aIndex;  
          end;
        eioHistory:
          begin
            if aIndex = -1 
              then Items.Insert(0, aColorString)
					    else Items.Move(aIndex, 0);        
            ItemIndex := 0;
          end;
        eioSorted: 
          begin
            if aIndex = -1 then
              begin
                Items.Add(aColorString);
                TStringList(Items).Sort;
                ItemIndex := TStringList(Items).IndexOf(aColorString);
              end else 
              ItemIndex := aIndex;  
          end;
      end;
      if assigned(OnCustomColorChanged) then OnCustomColorChanged(self);
    end;
end;

procedure TECColorCombo.SetItemHeight(const AValue: Integer);
begin
  inherited SetItemHeight(AValue);
  FNeedMeasure := True;
end;

procedure TECColorCombo.SetPrefix(AValue: string);
var i: Integer;          
begin
  if FPrefix = AValue then exit;
  FPrefix := AValue;                          
  FNeedMeasure := True;
  if ColorLayout <> eclSystemBGR then
    for i := 0 to Items.Count - 1 do
      Items[i] := AValue + TrimColorString(Items[i]);
end;            

procedure Register;
begin
  {$I eceditbtns.lrs}
  RegisterComponents('EC-C', [TECSpeedBtn, TECEditBtn, TECColorBtn, TECComboBtn, TECColorCombo]);
end;

end.


