Files
lazarus-ccr/components/exctrls/source/excheckcombo.pas

1155 lines
30 KiB
ObjectPascal

unit ExCheckCombo;
{$mode objfpc}{$H+}
interface
uses
LCLIntf, LCLType,
Classes, SysUtils, Controls, StdCtrls, ImgList,
GroupedEdit, EditBtn, CheckLst, Forms;
type
{ TCheckComboBoxEx }
TCheckComboBoxEx = class;
TCheckItemChange = procedure(Sender: TObject; AIndex: Integer) of object;
TCheckComboBoxHintMode = (cbhmDefault, cbhmItems);
TCheckComboBoxExStrings = class(TStringList)
private
function GetChecked(AIndex: Integer): Boolean;
function GetEnabled(AIndex: Integer): Boolean;
function GetObj(AIndex: Integer): TObject;
function GetState(AIndex: Integer): TCheckboxState;
procedure SetChecked(AIndex: Integer; AValue: Boolean);
procedure SetEnabled(AIndex: Integer; AValue: Boolean);
procedure SetObj(AIndex: Integer; AValue: TObject);
procedure SetState(AIndex: Integer; AValue: TCheckboxState);
protected
procedure InsertItem(AIndex: Integer; const AText: string); override;
procedure InsertItem(AIndex: Integer; const AText: string; Obj: TObject); override;
public
destructor Destroy; override;
procedure Assign(ASource: TPersistent); override;
procedure Clear; override;
procedure Delete(AIndex: Integer); override;
property Checked[AIndex: Integer]: Boolean read GetChecked write SetChecked;
property Enabled[AIndex: Integer]: boolean read GetEnabled write SetEnabled;
property Objects[AIndex: Integer]: TObject read GetObj write SetObj;
property State[AIndex: Integer]: TCheckboxState read GetState write SetState;
end;
TCheckComboBoxExImageIndex = class(TPersistent)
private
FOwner: TCheckComboBoxEx;
FImgIndex: array[0..7] of TImageIndex;
function GetImgIndex(AIndex: Integer): TImageIndex;
procedure SetImgIndex(AIndex: Integer; AValue: TImageIndex);
public
constructor Create(AOwner: TCheckComboBoxEx);
property Owner: TCheckComboBoxEx read FOwner;
published
property NormalDown: TImageIndex index 0 read GetImgIndex write SetImgIndex default -1;
property HotDown: TImageIndex index 1 read GetImgIndex write SetImgIndex default -1;
property PressedDown: TImageIndex index 2 read GetImgIndex write SetImgIndex default -1;
property DisabledDown: TImageIndex index 3 read GetImgIndex write SetImgIndex default -1;
property NormalUp: TImageIndex index 4 read GetImgIndex write SetImgIndex default -1;
property HotUp: TImageIndex index 5 read GetImgIndex write SetImgIndex default -1;
property PressedUp: TImageIndex index 6 read GetImgIndex write SetImgIndex default -1;
property DisabledUp: TImageIndex index 7 read GetImgIndex write SetImgIndex default -1;
end;
TCheckComboBoxEx = class(TCustomEditButton)
private
FAllowGrayed: Boolean;
FAutoDropDown: Boolean;
FButtonWidth: Integer;
FCheckListBox: TCheckListBox;
FCloseUpTime: TDateTime;
FDelimiter: Char;
FDropDownCount: Integer;
FDropDownImageIndex: TCheckComboBoxExImageIndex;
FDroppedDown: Boolean;
FEscCancels: Boolean;
FItemIndex: Integer;
FItems: TCheckComboBoxExStrings;
FItemWidth: Integer;
FHintMode: TCheckComboBoxHintMode;
FSavedChecks: array of Integer;
FSorted: Boolean;
FOnChange: TNotifyEvent;
FOnCloseUp: TNotifyEvent;
FOnDropDown: TNotifyEvent;
FOnItemChange: TCheckItemChange;
FOnItemClick: TCheckListClicked;
procedure ButtonMouseDownHandler(Sender: TObject;
{%H-}AButton: TMouseButton; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
procedure CheckComboBoxFormCloseHandler(Sender: TObject; var CloseAction: TCloseAction);
function GetButtonWidth: Integer;
function GetChecked(AIndex: Integer): Boolean;
function GetImages: TCustomImagelist;
function GetImageWidth: Integer;
function GetItemEnabled(AIndex: Integer): Boolean;
function GetItems: TStrings;
function GetState(AIndex: Integer): TCheckBoxState;
procedure ItemClickHandler(Sender: TObject; AIndex: Integer);
procedure ItemsToCheckListbox(ACheckListbox: TCheckListbox);
procedure SetAllowGrayed(AValue: Boolean);
procedure SetButtonWidth(AValue: Integer);
procedure SetChecked(AIndex: Integer; const AValue: Boolean);
procedure SetDelimiter(AValue: Char);
procedure SetDropDownCount(AValue: Integer);
procedure SetImages(AValue: TCustomImageList);
procedure SetImageWidth(AValue: Integer);
procedure SetItemEnabled(AIndex: Integer; const AValue: Boolean);
procedure SetItemIndex(AValue: Integer);
procedure SetItems(const AValue: TStrings);
procedure SetItemWidth(AValue: Integer);
procedure SetSorted(AValue: Boolean);
procedure SetState(AIndex: Integer; const AValue: TCheckboxState);
protected
procedure ButtonClick; override;
procedure CloseUp;
function CreateBuddy: TControl; override;
procedure CreateHandle; override;
procedure DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
const AXProportion, AYProportion: Double); override;
procedure DoEnter; override;
procedure DoChange; virtual;
procedure DoItemChange(AIndex: Integer); virtual;
procedure DoCloseUp; virtual;
procedure DoDropDown; virtual;
procedure DoItemClick(AIndex: Integer); virtual;
procedure DoOnShowHint(HintInfo: PHintInfo); override;
procedure EditKeyDown(var Key: Word; Shift: TShiftState); override;
function GetBuddyClassType: TControlClass; override;
function GetEditorClassType: TGEEditClass; override;
procedure InvalidateButton; virtual;
procedure RestoreInitialChecks;
procedure SaveInitialChecks;
procedure SetBiDiMode(AValue: TBiDiMode); override;
procedure ShowPopup;
procedure UpdateCaption;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function AddItem(AText: String; AChecked: Boolean = false; AEnabled: Boolean = true): Integer;
procedure CheckAll(AState: TCheckBoxState; AAllowGrayed: Boolean = true; AAllowDisabled: Boolean = true);
procedure Clear;
procedure Toggle(AIndex: Integer);
// public properties
property Checked[AIndex: Integer]: Boolean read GetChecked write SetChecked;
property DroppedDown: Boolean read FDroppedDown;
property ItemEnabled[AIndex: Integer]: Boolean read GetItemEnabled write SetItemEnabled;
property Text;
published
property AllowGrayed: Boolean read FAllowGrayed write SetAllowGrayed default false;
property AutoDropDown: Boolean read FAutoDropDown write FAutoDropDown default false;
property ButtonWidth: Integer read FButtonWidth write SetButtonWidth default -1;
property Delimiter: char read FDelimiter write SetDelimiter default ';';
property DropDownCount: Integer read FDropDownCount write SetDropDownCount default 8;
property DropDownImageIndex: TCheckComboBoxExImageIndex read FDropDownImageIndex write FDropDownImageIndex;
property EscCancels: Boolean read FEscCancels write FEscCancels default true;
property HintMode: TCheckComboBoxHintMode read FHintMode write FHintMode default cbhmDefault;
property Images: TCustomImageList read GetImages write SetImages;
property ImageWidth: Integer read GetImageWidth write SetImageWidth default 0;
property ItemIndex: Integer read FItemIndex write SetItemIndex default -1;
property Items: TStrings read GetItems write SetItems;
property ItemWidth: Integer read FItemWidth write FItemWidth default 0;
property Sorted: Boolean read FSorted write SetSorted default false;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
property OnCloseUp: TNotifyEvent read FOnCloseUp write FOnCloseUp;
property OnDropDown: TNotifyEvent read FOnDropDown write FOnDropDown;
property OnItemChange: TCheckItemChange read FOnItemChange write FOnItemChange;
property OnItemClick: TCheckListClicked read FOnItemClick write FOnItemClick;
// inherited properties
property Align;
property Anchors;
property AutoSize;
property BiDiMode;
property BorderSpacing;
property Color;
property Constraints;
property DragCursor;
property DragKind;
property DragMode;
property Enabled;
property Font;
property Hint;
property ParentBidiMode;
property ParentColor;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property Spacing default 0;
property TabOrder;
property TabStop;
property TextHint;
property Visible;
// inherited events
property OnChangeBounds;
property OnClick;
property OnContextPopup;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseEnter;
property OnMouseLeave;
property OnMouseMove;
property OnMouseUp;
property OnStartDrag;
end;
implementation
uses
Buttons, Themes, WSForms;
type
TCustomListboxHelper = class helper for TCustomListbox
public
function CalculateStandardItemHeight: Integer;
end;
function TCustomListboxHelper.CalculateStandardItemHeight: Integer;
begin
Result := inherited;
end;
{ TCCBItem } // CCB = CheckComboBox
type
TCCBItem = class
Caption: String;
State: TCheckboxState;
Enabled: Boolean;
Obj: TObject;
constructor Create;
end;
constructor TCCBItem.Create;
begin
State := cbUnchecked;
Enabled := true;
end;
{ TCheckComboBoxExStrings }
destructor TCheckComboBoxExStrings.Destroy;
begin
Clear;
inherited;
end;
procedure TCheckComboBoxExStrings.Assign(ASource: TPersistent);
var
i: Integer;
begin
inherited Assign(ASource);
if (ASource is TCheckComboBoxExStrings) then
for i := 0 to Count-1 do
begin
State[i] := TCheckComboBoxExStrings(ASource).State[i];
Enabled[i] := TCheckComboBoxExStrings(ASource).Enabled[i];
end;
end;
procedure TCheckComboBoxExStrings.Clear;
var
i: Integer;
begin
for i := 0 to Count-1 do
TCCBItem(inherited Objects[i]).Free;
inherited;
end;
procedure TCheckComboBoxExStrings.Delete(AIndex: Integer);
begin
TCCBItem(inherited Objects[AIndex]).Free;
inherited;
end;
function TCheckComboBoxExStrings.GetChecked(AIndex: Integer): Boolean;
begin
Result := GetState(AIndex) = cbChecked;
end;
function TCheckComboBoxExStrings.GetEnabled(AIndex: Integer): Boolean;
begin
Result := TCCBItem(inherited Objects[AIndex]).Enabled;
end;
function TCheckComboBoxExStrings.GetObj(AIndex: Integer): TObject;
begin
Result := TCCBItem(inherited Objects[AIndex]).Obj;
end;
function TCheckComboBoxExStrings.GetState(AIndex: Integer): TCheckboxState;
begin
Result := TCCBItem(inherited Objects[AIndex]).State;
end;
procedure TCheckComboBoxExStrings.InsertItem(AIndex: Integer; const AText: String);
begin
inherited InsertItem(AIndex, AText, TCCBItem.Create);
end;
procedure TCheckComboBoxExStrings.InsertItem(AIndex: Integer; const AText: String;
Obj: TObject);
begin
InsertItem(AIndex, AText);
TCCBItem(inherited Objects[AIndex]).Obj := Obj;
end;
procedure TCheckComboBoxExStrings.SetChecked(AIndex: Integer; AValue: Boolean);
begin
if AValue then
SetState(AIndex, cbChecked)
else
SetState(AIndex, cbUnchecked);
end;
procedure TCheckComboBoxExStrings.SetEnabled(AIndex: Integer; AValue: Boolean);
var
item: TCCBItem;
begin
item := TCCBItem(inherited Objects[AIndex]);
if item.Enabled = AValue then exit;
item.Enabled := AValue;
end;
procedure TCheckComboBoxExStrings.SetObj(AIndex: Integer; AValue: TObject);
begin
TCCBItem(inherited Objects[AIndex]).Obj := AValue;
end;
procedure TCheckComboBoxExStrings.SetState(AIndex: Integer; AValue: TCheckBoxState);
var
item: TCCBItem;
begin
item := TCCBItem(inherited Objects[AIndex]);
if item.State = AValue then exit;
item.State := AValue;
end;
{ TCCBEdit }
type
TCCBEdit = class(TEbEdit)
protected
procedure DoOnShowHint(HintInfo: PHintInfo); override;
end;
procedure TCCBEdit.DoOnShowHint(HintInfo: PHintInfo);
begin
(Parent as TCheckComboBoxEx).DoOnShowHint(HintInfo);
end;
{ TCCBButton }
type
TCCBButton = class(TSpeedButton)
private
procedure SetHotImageIndex;
procedure SetImageIndex(AEnabledIndex, ADisabledIndex: Integer; Up: Boolean);
procedure SetNormalImageIndex;
procedure SetPressedImageIndex;
protected
procedure DoOnShowHint(HintInfo: PHintInfo); override;
procedure MouseEnter; override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
procedure MouseLeave; override;
procedure Paint; override;
public
procedure UpdateImageIndex;
end;
procedure TCCBButton.DoOnShowHint(HintInfo: PHintInfo);
begin
(Parent as TCheckComboBoxEx).DoOnShowHint(HintInfo);
end;
procedure TCCBButton.MouseEnter;
begin
inherited;
SetHotImageIndex;
end;
procedure TCCBButton.MouseDown(Button: TMouseButton; Shift: TShiftState;
X,Y: Integer);
begin
inherited;
if (Button = mbLeft) and (Shift = []) then
SetPressedImageIndex;
end;
procedure TCCBButton.MouseLeave;
begin
inherited;
SetNormalImageIndex;
end;
procedure TCCBButton.Paint;
const
DETAILS: array[TButtonState] of TThemedComboBox = (
tcDropDownButtonNormal, // bsUp = button is up
tcDropDownButtonDisabled, // bsDisabled = button is disabled
tcDropDownButtonPressed, // bsPressed = button is down
tcDropDownButtonPressed, // (not used)
tcDropDownButtonHot // bsHot = mouse is under mouse
);
var
detail: TThemedElementDetails;
begin
inherited Paint;
if Images = nil then
begin
detail := ThemeServices.GetElementDetails(DETAILS[FState]);
ThemeServices.DrawElement(Canvas.Handle, detail, ClientRect);
end;
end;
procedure TCCBButton.SetHotImageIndex;
begin
with Parent as TCheckComboBoxEx do
if DroppedDown then
Self.SetImageIndex(DropDownImageIndex.HotUp, DropDownImageIndex.DisabledUp, true)
else
Self.SetImageIndex(DropDownImageIndex.HotDown, DropDownImageIndex.DisabledDown, false);
end;
procedure TCCBButton.SetImageIndex(AEnabledIndex, ADisabledIndex: Integer;
Up: Boolean);
var
idx: Integer;
begin
if Parent.Enabled then
begin
if AEnabledIndex > -1 then
ImageIndex := AEnabledIndex
else
ImageIndex := (Parent as TCheckComboBoxEx).DropDownImageIndex.NormalDown;
end else
begin
if ADisabledIndex > -1 then
ImageIndex := ADisabledIndex
else
begin
idx := -1;
if Up then
idx := (Parent as TCheckComboBoxEx).DropDownImageIndex.NormalUp;
if idx = -1 then
idx := (Parent as TCheckComboBoxEx).DropDownImageIndex.NormalDown;
ImageIndex := idx;
end;
end;
end;
procedure TCCBButton.SetNormalImageIndex;
begin
with Parent as TCheckComboBoxEx do
if DroppedDown then
Self.SetImageIndex(DropDownImageIndex.NormalUp, DropDownImageIndex.DisabledUp, true)
else
Self.SetImageIndex(DropDownImageIndex.NormalDown, DropDownImageIndex.DisabledDown, false);
end;
procedure TCCBButton.SetPressedImageIndex;
begin
with Parent as TCheckComboBoxEx do
if DroppedDown then
Self.SetImageIndex(DropDownImageIndex.PressedUp, DropDownImageIndex.DisabledUp, true)
else
Self.SetImageIndex(DropDownImageIndex.PressedDown, DropDownImageIndex.DisabledDown, false);
end;
procedure TCCBButton.UpdateImageIndex;
var
P: TPoint;
begin
P := ScreenToClient(Mouse.CursorPos);
if PtInRect(BoundsRect, P) then
SetHotImageIndex
else
SetNormalImageIndex;
end;
{ TCheckComboBoxForm }
type
TCheckComboBoxForm = class(TForm)
private
FCaller: TControl;
FCheckListBox: TCheckListBox;
FDropDownCount: Integer;
FStdItemHeight: Integer;
protected
procedure ActivateDoubleBuffered;
procedure DblClickHandler(Sender: TObject);
procedure Deactivate; override;
procedure KeepInView(APopupOrigin: TPoint);
procedure KeyDownHandler(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure MeasureHeight(out AHeight: Integer);
public
constructor CreateNew(AOwner: TComponent; Num: Integer = 0); override;
procedure Initialize(ADropdownCount: Integer; APosition: TPoint; AWidth: Integer);
end;
constructor TCheckComboBoxForm.CreateNew(AOwner: TComponent; Num: Integer = 0);
begin
inherited CreateNew(AOwner, Num);
BorderStyle := bsNone;
FCheckListbox := TCheckListbox.Create(self);
FCheckListbox.Align := alClient;
FCheckListbox.Parent := self;
FCheckListbox.OnDblClick := @DblClickHandler;
FCheckListbox.OnKeyDown := @KeyDownHandler;
FDropDownCount := 8;
FStdItemHeight := FCheckListbox.CalculateStandardItemHeight;
end;
procedure TCheckComboBoxForm.ActivateDoubleBuffered;
begin
DoubleBuffered := TWSCustomFormClass(WidgetSetClass).GetDefaultDoubleBuffered;
end;
procedure TCheckComboBoxForm.Deactivate;
begin
Close;
end;
procedure TCheckComboboxForm.DblClickHandler(Sender: TObject);
begin
{$IFDEF LCLCocoa}
Free;
{$ELSE}
Close;
{$ENDIF}
end;
procedure TCheckComboBoxForm.Initialize(ADropDownCount: Integer;
APosition: TPoint; AWidth: Integer);
var
h: Integer;
begin
FDropDownCount := ADropDownCount;
MeasureHeight(h);
SetBounds(APosition.X, APosition.Y, AWidth, h);
KeepInView(APosition);
AutoScroll := FCheckListBox.Items.Count > FDropDownCount;
end;
procedure TCheckComboBoxForm.KeepInView(APopupOrigin: TPoint);
var
R: TRect;
begin
R := Screen.MonitorFromPoint(APopupOrigin).WorkAreaRect; // take care of taskbar
if APopupOrigin.X + Width > R.Right then
Left := R.Right - Width
else if APopupOrigin.X < R.Left then
Left := R.Left
else
Left := APopupOrigin.X;
if APopupOrigin.Y + Height > R.Bottom then
begin
if Assigned(FCaller) then
Top := APopupOrigin.Y - FCaller.Height - Height
else
Top := R.Bottom - Height;
end else if APopupOrigin.Y < R.Top then
Top := R.Top
else
Top := APopupOrigin.Y;
if Left < R.Left then Left := 0;
if Top < R.Top then Top := 0;
end;
procedure TCheckComboBoxForm.KeyDownHandler(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
inherited;
if (Key = VK_ESCAPE) and TCheckComboBoxEx(FCaller).EscCancels then
TCheckComboBoxEx(FCaller).RestoreInitialChecks;
if (Key = VK_ESCAPE) or (Key = VK_TAB) or (Key = VK_RETURN) or
(((Key = VK_UP) or (Key = VK_DOWN)) and (Shift = [ssAlt]))
then
Close;
end;
procedure TCheckComboBoxForm.MeasureHeight(out AHeight: Integer);
var
i, h, n: Integer;
begin
AHeight := 0;
n := FCheckListbox.Items.Count;
if FDropDownCount < n then
n := FDropDownCount;
for i := 0 to n-1 do
begin
h := -1;
FCheckListbox.MeasureItem(i, h);
if h = -1 then
h := FStdItemHeight;
inc(AHeight, h);
end;
inc(AHeight, 6);
end;
{ TCheckComboBoxExImageIndex }
constructor TCheckComboBoxExImageIndex.Create(AOwner: TCheckComboBoxEx);
var
i: Integer;
begin
inherited Create;
FOwner := AOwner;
for i := 0 to High(FImgIndex) do
FImgIndex[i] := -1;
end;
function TCheckComboBoxExImageIndex.GetImgIndex(AIndex: Integer): TImageIndex;
begin
Result := FImgIndex[AIndex];
end;
procedure TCheckComboBoxExImageIndex.SetImgIndex(AIndex: Integer; AValue: TImageIndex);
begin
if FImgIndex[AIndex] = AValue then
exit;
FImgIndex[AIndex] := AValue;
TCheckComboBoxEx(FOwner).InvalidateButton;
end;
{ TCheckComboBoxEx }
constructor TCheckComboBoxEx.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Spacing := 0;
Button.Flat := true;
FButtonWidth := -1;
FDelimiter := ';';
FDropDownCount := 8;
FDropDownImageIndex := TCheckComboBoxExImageIndex.Create(self);
ImageIndex := FDropDownImageIndex.NormalDown;
FItems := TCheckComboBoxExStrings.Create;
FEscCancels := true;
end;
destructor TCheckComboBoxEx.Destroy;
begin
FItems.Free;
FDropdownImageIndex.Free;
inherited Destroy;
end;
function TCheckComboBoxEx.AddItem(AText: String; AChecked: Boolean = false;
AEnabled: Boolean = true): Integer;
var
j: Integer;
begin
j := FItems.Add(AText);
if AChecked then
FItems.State[j] := cbChecked
else
FItems.State[j] := cbUnchecked;
FItems.Enabled[j] := AEnabled;
if FDroppedDown then
with FCheckListBox do
begin
Result := Items.Add(AText);
Checked[Result] := AChecked;
ItemEnabled[Result] := AEnabled;
end;
UpdateCaption;
end;
procedure TCheckComboBoxEx.ButtonClick;
begin
{
if DroppedDown then
CloseUp
else
ShowPopup;
}
end;
procedure TCheckComboBoxEx.ButtonMouseDownHandler(Sender: TObject;
AButton: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if DroppedDown then
CloseUp
else
ShowPopup;
end;
procedure TCheckComboBoxEx.CheckAll(AState: TCheckBoxState;
AAllowGrayed: Boolean = true; AAllowDisabled: Boolean = true);
var
i: Integer;
begin
for i := 0 to FItems.Count-1 do
begin
if not FItems.Enabled[i] and not AAllowDisabled then
Continue;
if AState in [cbChecked, cbUnchecked] then
FItems.State[i] := AState
else if (AState = cbGrayed) then
begin
if AAllowGrayed then
FItems.State[i] := cbGrayed
else
FItems.State[i] := cbUnChecked;
end;
end;
if FDroppedDown then
with FCheckListBox do
CheckAll(AState, AAllowGrayed, AAllowDisabled);
UpdateCaption;
DoChange;
end;
procedure TCheckComboBoxEx.CheckComboBoxFormCloseHandler(Sender: TObject;
var CloseAction: TCloseAction);
begin
CloseUp;
FCheckListBox := nil;
CloseAction := caFree;
end;
procedure TCheckComboBoxEx.Clear;
begin
FItems.Clear;
if FDroppedDown then
FCheckListBox.Clear;
UpdateCaption;
DoChange;
end;
procedure TCheckComboBoxEx.CloseUp;
begin
FDroppedDown := false;
InvalidateButton;
UpdateCaption;
DoCloseUp;
FCloseUpTime := Now();
end;
function TCheckComboBoxEx.CreateBuddy: TControl;
begin
Result := inherited;
TSpeedButton(Result).OnMouseDown := @ButtonMouseDownHandler;
end;
procedure TCheckComboBoxEx.CreateHandle;
begin
inherited;
if FButtonWidth < 0 then
Buddy.Width := GetSystemMetrics(SM_CXVSCROLL);
end;
procedure TCheckComboBoxEx.DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
const AXProportion, AYProportion: Double);
begin
inherited;
if AMode in [lapAutoAdjustWithoutHorizontalScrolling, lapAutoAdjustForDPI] then
begin
if FButtonWidth < 0 then
Buddy.Width := GetSystemMetrics(SM_CXVSCROLL)
else
begin
FButtonWidth := round(AXProportion * FButtonWidth);
Buddy.Width := FButtonWidth;
end;
end;
end;
procedure TCheckComboBoxEx.DoChange;
begin
if Assigned(FOnChange) then FOnChange(self);
end;
procedure TCheckComboBoxEx.DoCloseUp;
begin
if Assigned(FOnCloseUp) then FOnCloseUp(self);
end;
procedure TCheckComboBoxEx.DoDropDown;
begin
if Assigned(FOnDropDown) then FOnDropDown(self);
end;
procedure TCheckComboBoxEx.DoEnter;
begin
inherited;
if FAutoDropDown then ShowPopup;
end;
procedure TCheckComboBoxEx.DoItemChange(AIndex: Integer);
begin
if Assigned(FOnItemChange) then
FOnItemChange(self, AIndex);
end;
procedure TCheckComboBoxEx.DoItemClick(AIndex: Integer);
begin
if Assigned(FOnItemClick) then
FOnItemClick(self, AIndex);
end;
procedure TCheckComboBoxEx.DoOnShowHint(HintInfo: PHintInfo);
begin
if FHintMode = cbhmItems then
HintInfo^.HintStr := StringReplace(Text, FDelimiter+' ', LineEnding, [rfReplaceAll]);
inherited;
end;
procedure TCheckComboBoxEx.EditKeyDown(var Key: Word; Shift: TShiftState);
begin
if (Key = VK_TAB) then
begin
inherited;
exit;
end;
if (Key = VK_RETURN) or (Key = VK_SPACE) or
(((Key = VK_DOWN) or (Key = VK_UP)) and (Shift = [ssAlt]))
then
ShowPopup;
// Kill all other keys
Key := 0;
end;
function TCheckComboBoxEx.GetBuddyClassType: TControlClass;
begin
Result := TCCBButton;
end;
function TCheckComboBoxEx.GetButtonWidth: Integer;
begin
Result := Buddy.Width;
end;
function TCheckComboBoxEx.GetChecked(AIndex: Integer): Boolean;
begin
Result := FItems.Checked[AIndex];
end;
function TCheckComboBoxEx.GetEditorClassType: TGEEditClass;
begin
Result := TCCBEdit;
end;
function TCheckComboBoxEx.GetImages: TCustomImageList;
begin
Result := (Buddy as TSpeedButton).Images;
end;
function TCheckComboBoxEx.GetImageWidth: Integer;
begin
Result := (Buddy as TSpeedButton).ImageWidth;
end;
function TCheckComboBoxEx.GetItemEnabled(AIndex: Integer): Boolean;
begin
Result := FItems.Enabled[AIndex];
end;
function TCheckComboBoxEx.GetItems: TStrings;
begin
Result := FItems;
end;
function TCheckComboBoxEx.GetState(AIndex: Integer): TCheckboxState;
begin
Result := FItems.State[AIndex];
end;
procedure TCheckComboBoxEx.InvalidateButton;
begin
with (Buddy as TCCBButton) do
UpdateImageIndex;
end;
procedure TCheckComboBoxEx.ItemClickHandler(Sender: TObject; AIndex: Integer);
begin
UpdateCaption;
DoChange;
DoItemChange(AIndex);
DoItemClick(AIndex);
end;
procedure TCheckComboBoxEx.ItemsToCheckListbox(ACheckListbox: TCheckListbox);
var
i: Integer;
begin
ACheckListbox.Items.BeginUpdate;
try
ACheckListbox.Items.Clear;
for i := 0 to FItems.Count-1 do
begin
ACheckListbox.Items.Add(FItems[i]);
ACheckListbox.State[i] := FItems.State[i];
ACheckListbox.ItemEnabled[i] := FItems.Enabled[i];
end;
if (FItemIndex > -1) and (FItemIndex < ACheckListbox.Count) then
ACheckListbox.ItemIndex := FItemIndex;
finally
ACheckListbox.Items.EndUpdate;
end;
end;
procedure TCheckComboBoxEx.RestoreInitialChecks;
var
i: Integer;
begin
for i := 0 to FItems.Count-1 do
FItems.Checked[i] := false;
for i := 0 to High(FSavedChecks) do
FItems.Checked[FSavedChecks[i]] := true;
end;
procedure TCheckComboBoxEx.SaveInitialChecks;
var
i, n: Integer;
begin
SetLength(FSavedChecks, FItems.Count);
n := 0;
for i := 0 to FItems.Count-1 do
if FItems.Checked[i] then
begin
FSavedChecks[n] := i;
inc(n);
end;
SetLength(FSavedChecks, n);
end;
procedure TCheckComboBoxEx.SetAllowGrayed(AValue: Boolean);
begin
if FAllowGrayed = AValue then exit;
FAllowGrayed := AValue;
if FDroppedDown then
FCheckListBox.AllowGrayed := AValue;
end;
procedure TCheckComboBoxEx.SetBiDiMode(AValue: TBiDiMode);
begin
if AValue = BiDiMode then exit;
inherited SetBiDiMode(AValue);
if FDroppedDown then
FCheckListBox.BiDiMode := AValue;
if IsRightToLeft then
Buddy.Align := alLeft
else
Buddy.Align := alRight;
UpdateCaption;
end;
procedure TCheckComboBoxEx.SetButtonWidth(AValue: Integer);
begin
if FButtonWidth = AValue then
exit;
FButtonWidth := AValue;
if FButtonWidth < 0 then
Buddy.Width := GetSystemMetrics(SM_CXVSCROLL)
else
Buddy.Width := FButtonWidth;
end;
procedure TCheckComboBoxEx.SetChecked(AIndex: Integer; const AValue: Boolean);
begin
if FItems.Checked[AIndex] = AValue then
exit;
if AValue then
FItems.State[AIndex] := cbChecked
else
FItems.State[AIndex] := cbUnchecked;
if FDroppedDown then
FCheckListBox.Checked[AIndex] := AValue;
UpdateCaption;
DoChange;
DoItemChange(AIndex);
end;
procedure TCheckComboBoxEx.SetDelimiter(AValue: Char);
begin
if FDelimiter = AValue then exit;
FDelimiter := AValue;
UpdateCaption;
end;
procedure TCheckComboBoxEx.SetDropDownCount(AValue: Integer);
begin
if (FDropDownCount = AValue) or (AValue <= 0) then exit;
FDropDownCount := AValue;
end;
procedure TCheckComboBoxEx.SetImages(AValue: TCustomImageList);
begin
(Buddy as TSpeedButton).Images := AValue;
end;
procedure TCheckComboBoxEx.SetImageWidth(AValue: Integer);
begin
(Buddy as TSpeedButton).ImageWidth := AValue;
end;
procedure TCheckComboBoxEx.SetItemEnabled(AIndex: Integer; const AValue: Boolean);
begin
if FItems.Enabled[AIndex] = AValue then exit;
FItems.Enabled[AIndex] := AValue;
if FDroppedDown then
FCheckListbox.ItemEnabled[AIndex] := AValue;
end;
procedure TCheckComboBoxEx.SetItemIndex(AValue: Integer);
begin
if FItemIndex = AValue then exit;
FItemIndex := AValue;
if FDroppedDown then
FCheckListBox.ItemIndex := AValue;
end;
procedure TCheckComboBoxEx.SetItems(const AValue: TStrings);
begin
FItems.Assign(AValue);
if FDroppedDown then
FCheckListBox.Items.Assign(AValue);
end;
procedure TCheckComboBoxEx.SetItemWidth(AValue: Integer);
begin
if FItemWidth = AValue then exit;
if AValue < 0 then
FItemWidth := 0
else
FItemWidth := AValue;
end;
procedure TCheckComboBoxEx.SetSorted(AValue: Boolean);
begin
if AValue = FSorted then
exit;
FSorted := AValue;
if FSorted then
FItems.Sort;
UpdateCaption;
end;
procedure TCheckComboBoxEx.SetState(AIndex: Integer; const AValue: TCheckBoxState);
begin
if FItems.State[AIndex] = AValue then
exit;
FItems.State[AIndex] := AValue;
if FDroppedDown then
FCheckListbox.State[AIndex] := AValue;
DoChange;
DoItemChange(AIndex);
end;
procedure TCheckComboBoxEx.ShowPopup;
const
MILLISECOND = 1.0/(24*60*60*100);
DELAY = 10*MILLISECOND;
var
PopupOrigin: TPoint;
PopupWidth: Integer;
F: TCheckComboBoxForm;
begin
if FItems.Count = 0 then
exit;
if Now() - FCloseUpTime < DELAY then
exit;
F := TCheckComboBoxForm.CreateNew(Application);
F.FCaller := Self;
F.BiDiMode := BiDiMode;
if DoubleBuffered then
F.ActivateDoubleBuffered;
F.FCheckListBox.AllowGrayed := FAllowGrayed;
PopupOrigin := ControlToScreen(Point(0, Height));
if FItemWidth > 0 then
PopupWidth := FItemWidth
else
PopupWidth := Self.Width;
ItemsToCheckListbox(F.FCheckListbox);
F.Initialize(FDropDownCount, PopupOrigin, PopupWidth);
SaveInitialChecks;
F.FCheckListbox.OnItemClick := @ItemClickHandler;
F.OnClose := @CheckComboBoxFormCloseHandler;
F.Show;
FDroppedDown := true;
FCheckListbox := F.FCheckListbox;
InvalidateButton;
DoDropDown;
end;
procedure TCheckComboBoxEx.Toggle(AIndex: Integer);
begin
SetChecked(AIndex, not GetChecked(AIndex));
end;
procedure TCheckComboBoxEx.UpdateCaption;
var
i: Integer;
s: String;
begin
if FDroppedDown then
for i := 0 to FCheckListBox.Items.Count - 1 do
FItems.State[i] := FCheckListbox.State[i];
s := '';
if IsRightToLeft then
begin
for i := FItems.Count-1 downto 0 do
if FItems.Checked[i] then
s := s + FItems[i] + FDelimiter + ' '
end else
begin
for i := 0 to FItems.Count-1 do
if FItems.Checked[i] then
s := s + FItems[i] + FDelimiter + ' ';
end;
if s <> '' then
SetLength(s, Length(s)-2);
Text := s;
end;
end.