lazarus-ccr/components/exctrls/source/excombo.pas
2021-10-11 20:33:15 +00:00

1037 lines
29 KiB
ObjectPascal

{ TColumnCombo is a text-only combobox that displays its dropdown items list in
single phrase columns, which are parsed according to the (Char) Delimiter
property.
Column width in the dropdown is adjusted automatically to accomomodate the
longest word/phrase in each column.
The number of columns shown depends entirely on the number of delimiters found
in each listed item, hence is an unpublished read-only property.
The default delimiter is the comma.
There is a display property -- commented out -- ShowColSeparators (False by
default) which displays vertical lines between the listed columns. It seems ugly to me,
so I disabled it, but you can re-enable it if you want.
The ColumnMargin property allows for adjustment of all column widths by a fixed amount.
H Page-Clark 2013
License:
This library is free software; you can redistribute it and/or modify it
under the same terms as the Lazarus Component Library (LCL)
See the file COPYING.modifiedLGPL.txt, included in the Lazarus distribution,
for details about the license.
}
unit ExCombo;
{$mode objfpc}{$H+}
interface
uses
LCLIntf, LCLType, LMessages, LazLoggerBase,
Classes, SysUtils, Graphics, Types,
StdCtrls, Controls, Forms;
type
TColumnComboBoxEx = class(TCustomComboBox)
private
FColumnCount: Integer;
FColumnMargin: Integer;
FDelimiter: AnsiChar;
FOffsets: TIntegerDynArray;
FParser: TStringList;
FColSeparatorColor: TColor;
FShowColSeparators: Boolean;
FSelectedColor: TColor;
FSelectedTextColor: TColor;
FTextHeight: Integer;
function ColumnMarginStored: Boolean;
function GetTextSize(const aText: String): TSize;
function GetColumnCount: Integer;
function GetDelimiteds(const aLine: String): TStringArray;
procedure SetColSeparatorColor(AValue: TColor);
procedure SetColumnMargin(aValue: Integer);
procedure SetDelimiter(aValue: AnsiChar);
procedure SetOffsets;
procedure SetShowColSeparators(aValue: Boolean);
procedure SetSelectedColor(AValue: TColor);
procedure SetSelectedTextColor(AValue: TColor);
protected
procedure CreateHandle; override;
procedure DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
const AXProportion, AYProportion: Double); override;
procedure DrawItem(Index: Integer; ARect: TRect; State: TOwnerDrawState); override;
class function GetControlClassDefaultSize: TSize; override;
procedure GetItems; override;
procedure InitializeWnd; override;
procedure FontChanged(Sender: TObject); override;
procedure SetItems(const Value: TStrings); override;
procedure SetStyle(AValue: TComboBoxStyle); override;
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
property ColumnCount: Integer read FColumnCount;
published
// new properties
property ColumnMargin: Integer read FColumnMargin write SetColumnMargin stored ColumnMarginStored;
property ColSeparatorColor: TColor read FColSeparatorColor write SetColSeparatorColor default clSilver;
property Delimiter: AnsiChar read FDelimiter write SetDelimiter default ',';
property SelectedColor: TColor read FSelectedColor write SetSelectedColor default clHighlight;
property SelectedTextColor: TColor read FSelectedTextColor write SetSelectedTextColor default clHighlightText;
property ShowColSeparators: Boolean read FShowColSeparators write SetShowColSeparators default False;
// inherited comboBox properties
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 default clWindow;
property Constraints;
property DragCursor;
property DragKind;
property DragMode;
property DropDownCount;
property Enabled;
property Font;
property ItemHeight;
property ItemIndex;
property Items;
property ItemWidth;
property MaxLength;
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;
property ParentBidiMode;
property ParentColor;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property Sorted;
property Style default csOwnerDrawFixed;
property TabOrder;
property TabStop;
property Text;
property Visible;
end;
{ TCustomCheckComboBoxEx }
TCheckComboItemState = class
public
State: TCheckBoxState;
Enabled: Boolean;
Data: TObject;
end;
TCheckItemChange = procedure(Sender: TObject; AIndex: Integer) of object;
TCustomCheckComboBoxEx = class(TCustomComboBox)
private
FAllowGrayed: Boolean;
FOnItemChange: TCheckItemChange;
procedure AsyncCheckItemStates(Data: PtrInt);
function GetChecked(AIndex: Integer): Boolean;
function GetCount: Integer;
function GetItemEnabled(AIndex: Integer): Boolean;
function GetObject(AIndex: Integer): TObject;
function GetState(AIndex: Integer): TCheckBoxState;
procedure SetChecked(AIndex: Integer; AValue: Boolean);
procedure SetItemEnabled(AIndex: Integer; AValue: Boolean);
procedure SetObject(AIndex: Integer; AValue: TObject);
procedure SetState(AIndex: Integer; AValue: TCheckBoxState);
protected
FCheckHighlight: Boolean;
FCheckSize: TSize;
FDropped: Boolean;
FHilightedIndex: Integer;
FHiLiteLeft: Integer;
FHiLiteRight: Integer;
FNeedMeasure: Boolean;
FRejectDropDown: Boolean;
FRejectToggleOnSelect: Boolean;
FRightToLeft: Boolean;
FTextHeight: SmallInt;
procedure CMBiDiModeChanged(var Message: TLMessage); message CM_BIDIMODECHANGED;
procedure ClearItemStates;
procedure CloseUp; override;
procedure DrawItem(Index: Integer; ARect: TRect; State: TOwnerDrawState); override;
procedure DropDown; override;
procedure FontChanged(Sender: TObject); override;
procedure InitializeWnd; override;
procedure InitItemStates;
procedure CheckItemStates;
procedure QueueCheckItemStates;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure Loaded; override;
procedure MouseLeave; override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure SetItemHeight(const AValue: Integer); override;
procedure SetItems(const Value: TStrings); override;
procedure Select; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure AddItem(const AItem: string; AState: TCheckBoxState; AEnabled: Boolean = True); reintroduce;
procedure AssignItems(AItems: TStrings);
procedure Clear; override;
procedure DeleteItem(AIndex: Integer);
procedure CheckAll(AState: TCheckBoxState; AAllowGrayed: Boolean = True; AAllowDisabled: Boolean = True);
procedure Toggle(AIndex: Integer);
property AllowGrayed: Boolean read FAllowGrayed write FAllowGrayed default False;
property Count: Integer read GetCount;
property Checked[AIndex: Integer]: Boolean read GetChecked write SetChecked;
property ItemEnabled[AIndex: Integer]: Boolean read GetItemEnabled write SetItemEnabled;
property Objects[AIndex: Integer]: TObject read GetObject write SetObject;
property State[AIndex: Integer]: TCheckBoxState read GetState write SetState;
property OnItemChange: TCheckItemChange read FOnItemChange write FOnItemChange;
end;
{ TCheckComboBox }
TCheckComboBoxEx = class(TCustomCheckComboBoxEx)
published
property Align;
property AllowGrayed;
property Anchors;
property ArrowKeysTraverseList;
property AutoDropDown;
property AutoSize;
property BidiMode;
property BorderSpacing;
property BorderStyle;
property Color;
property Constraints;
property Count;
property DragCursor;
property DragKind;
property DragMode;
property DropDownCount;
property Enabled;
property Font;
property ItemHeight;
property ItemIndex;
property Items;
property ItemWidth;
property MaxLength;
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 OnItemChange;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseEnter;
property OnMouseLeave;
property OnMouseMove;
property OnMouseUp;
property OnMouseWheel;
property OnMouseWheelDown;
property OnMouseWheelUp;
property OnStartDrag;
property OnSelect;
property OnUTF8KeyPress;
property ParentBidiMode;
property ParentColor;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property Sorted;
property TabOrder;
property TabStop;
property Text;
property TextHint;
property Visible;
end;
implementation
uses
GraphUtil, Themes;
const
DEFAULT_COLUMN_MARGIN = 4;
{ TColumnCombo }
constructor TColumnComboBoxEx.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
Color := clWindow;
FParser := TStringList.Create;
FColumnMargin := DEFAULT_COLUMN_MARGIN;
FColumnCount := 0;
FColSeparatorColor := clSilver;
FDelimiter := ',';
FShowColSeparators := False;
FSelectedColor := clHighlight;
FSelectedTextColor := clHighlightText;
SetStyle(csOwnerDrawFixed);
FOffsets := nil;
FColumnCount := 0;
end;
destructor TColumnComboBoxEx.Destroy;
begin
FParser.Free;
inherited Destroy;
end;
function TColumnComboBoxEx.ColumnMarginStored: Boolean;
begin
Result := FColumnMargin <> Scale96ToFont(DEFAULT_COLUMN_MARGIN);
end;
procedure TColumnComboBoxEx.CreateHandle;
begin
inherited;
SetOffsets;
end;
procedure TColumnComboBoxEx.DoAutoAdjustLayout(
const AMode: TLayoutAdjustmentPolicy; const AXProportion, AYProportion: Double
);
begin
inherited DoAutoAdjustLayout(AMode, AXProportion, AYProportion);
if AMode in [lapAutoAdjustWithoutHorizontalScrolling, lapAutoAdjustForDPI] then
begin
if ColumnMarginStored then
FColumnMargin := Round(FColumnMargin * AXProportion);
Invalidate;
end;
end;
procedure TColumnComboBoxEx.DrawItem(Index: Integer; ARect: TRect; State: TOwnerDrawState);
var
i, y, xl: Integer;
txt: String;
savedColor, savedFontColor: TColor;
begin
if Assigned(OnDrawItem) then
begin
OnDrawItem(Self, Index, ARect, State);
exit;
end;
savedColor := Canvas.Brush.Color;
savedFontColor := Canvas.Font.Color;
if DroppedDown then
begin
if (odSelected in State) then
begin
Canvas.Brush.Color := FSelectedColor;
Canvas.Font.Color := FSelectedTextColor;
end else
if (Canvas.Brush.Color <> Color) then
Canvas.Brush.Color := Color;
Canvas.Brush.Style := bsSolid;
Canvas.FillRect(ARect);
end else
Canvas.Font.Color := clWindowText;
if Index < 0 then
txt := ''
else
txt := Items[Index];
FParser.StrictDelimiter := FDelimiter <> ' ';
FParser.Delimiter := FDelimiter;
FParser.DelimitedText := txt;
y := ARect.Top + (ARect.Height - FTextHeight) shr 1;
Canvas.Brush.Style := bsClear; // transparent text background
if Assigned(FOffsets) then
begin
for i := 0 to FParser.Count-1 do
begin
xl := ARect.Left + FOffsets[i];
Canvas.TextOut(xl, y, FParser[i]);
end;
if FShowColSeparators then
begin
Canvas.Pen.Color := FColSeparatorColor;
for i := 1 to High(FOffsets) do
begin
xl := FOffsets[i];
Dec(xl, FColumnMargin);
Canvas.Line(xl, ARect.Top, xl, ARect.Bottom);
end;
end;
end
else
Canvas.TextOut(xl, y, txt);
Canvas.Brush.Color := savedColor;
Canvas.Font.Color := savedFontColor;
end;
procedure TColumnComboBoxEx.FontChanged(Sender: TObject);
begin
inherited FontChanged(Sender);
FTextHeight := Canvas.TextHeight('ŢÜ');
end;
function TColumnComboBoxEx.GetColumnCount: Integer;
var
i, tmp: Integer;
s: String;
function GetDelimCount: Integer;
var
p: Integer;
begin
Result := 0;
for p := 1 to Length(s) do
if s[p] = FDelimiter then
Inc(Result);
end;
begin
Result := 0;
for i := 0 to Items.Count-1 do
begin
s := Items[i];
tmp := GetDelimCount;
if Result < tmp then
Result := tmp;
end;
Inc(Result);
end;
class function TColumnComboBoxEx.GetControlClassDefaultSize: TSize;
begin
Result := inherited GetControlClassDefaultSize;
Result.cx := 200;
end;
function TColumnComboBoxEx.GetDelimiteds(const aLine: String): TStringArray;
var
p, start, resultIdx: Integer;
begin
Result := Nil;
SetLength(Result, FColumnCount);
start := 1;
resultIdx := 0;
for p := 1 to Length(aLine) do
begin
case (aLine[p] = FDelimiter) of
True: begin
Result[resultIdx] := Copy(aLine, start, p - start);
start := Succ(p);
Inc(resultIdx);
end;
False: ;
end;
end;
Result[resultIdx] := Copy(aLine, start, Length(aLine));
end;
procedure TColumnComboBoxEx.GetItems;
begin
inherited GetItems;
// SetOffsets;
end;
function TColumnComboBoxEx.GetTextSize(const aText: String): TSize;
var
drawFlags: LongWord = DT_CALCRECT or DT_NOPREFIX or DT_SINGLELINE;
r: TRect;
dc: HDC;
begin
r.Left := 0;
r.Top := 0;
dc := GetDC(GetParentForm(Self).Handle);
try
r.Right := 1000;
r.Bottom := 100;
DrawText(dc, PChar(aText), Length(aText), r, drawFlags);
if r.Right = 1000 then
r.Right := 0;
if r.Bottom = 100 then
r.Bottom := 0;
Result.Create(r.Width, r.Height);
finally
ReleaseDC(Parent.Handle, dc);
end;
end;
procedure TColumnComboBoxEx.InitializeWnd;
begin
inherited;
SetOffsets;
end;
procedure TColumnComboBoxEx.SetColSeparatorColor(AValue: TColor);
begin
if FColSeparatorColor <> AValue then
begin
FColSeparatorColor := AValue;
Invalidate;
end;
end;
procedure TColumnComboBoxEx.SetColumnMargin(aValue: Integer);
begin
if FColumnMargin <> aValue then
begin
FColumnMargin := aValue;
Invalidate;
end;
end;
procedure TColumnComboBoxEx.SetDelimiter(aValue: AnsiChar);
begin
if FDelimiter <> aValue then
begin
FDelimiter := aValue;
FColumnCount := GetColumnCount;
end;
end;
procedure TColumnComboBoxEx.SetItems(const Value: TStrings);
begin
inherited SetItems(Value);
SetOffsets;
end;
procedure TColumnComboBoxEx.SetOffsets;
var
widths: TIntegerDynArray;
i, j: Integer;
sa: TStringArray;
sz: TSize;
begin
if not Assigned(Parent) or (Items.Count = 0) then
Exit;
FColumnCount := GetColumnCount;
SetLength({%H-}widths, FColumnCount);
for i := 0 to Items.Count-1 do
begin
sa := GetDelimiteds(Items[i]);
FTextHeight := GetTextSize('ŢÜ').cy;
for j := 0 to High(sa) do
begin
sz := GetTextSize(sa[j]);
if widths[j] < sz.cx then
widths[j] := sz.cx;
end;
end;
SetLength(FOffsets, FColumnCount);
for j := 0 to High(FOffsets) do
case j of
0: FOffsets[j] := FColumnMargin;
else
FOffsets[j] := FOffsets[Pred(j)] + widths[Pred(j)] + FColumnMargin shl 1;
end;
end;
procedure TColumnComboBoxEx.SetSelectedColor(AValue: TColor);
begin
if FSelectedColor <> AValue then
begin
FSelectedColor := AValue;
Invalidate;
end;
end;
procedure TColumnComboBoxEx.SetSelectedTextColor(AValue: TColor);
begin
if FSelectedTextColor <> AValue then
begin
FSelectedTextColor := AValue;
Invalidate;
end;
end;
procedure TColumnComboBoxEx.SetShowColSeparators(aValue: Boolean);
begin
if FShowColSeparators <> aValue then
begin
FShowColSeparators := aValue;
Invalidate;
end;
end;
procedure TColumnComboBoxEx.SetStyle(AValue: TComboBoxStyle);
begin
if (AValue in [csSimple, csDropDown, csDropDownList]) then
raise Exception.Create('Only owner-draw styles allowed.');
inherited SetStyle(AValue);
end;
{ TCustomCheckComboBoxEx }
constructor TCustomCheckComboBoxEx.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
TStringList(Items).Duplicates:=dupIgnore;
Style:=csOwnerDrawFixed;
FNeedMeasure:=True;
FRejectToggleOnSelect:=True;
end;
destructor TCustomCheckComboBoxEx.Destroy;
begin
ClearItemStates;
inherited Destroy;
end;
procedure TCustomCheckComboBoxEx.AddItem(const AItem: string; AState: TCheckBoxState; AEnabled: Boolean);
var pItemState: TCheckComboItemState;
begin
pItemState:=TCheckComboItemState.Create;
pItemState.State:=aState;
pItemState.Enabled:=AEnabled;
pItemState.Data:=nil;
inherited AddItem(AItem, pItemState);
end;
procedure TCustomCheckComboBoxEx.AssignItems(AItems: TStrings);
begin
ClearItemStates;
Items.Assign(AItems);
InitItemStates;
end;
procedure TCustomCheckComboBoxEx.CheckAll(AState: TCheckBoxState; AAllowGrayed: Boolean;
AAllowDisabled: Boolean);
var i: Integer;
begin
for i:=0 to Items.Count-1 do
begin
if (AAllowGrayed or (State[i]<>cbGrayed)) and (AAllowDisabled or ItemEnabled[i])
then State[i]:=AState;
end;
end;
procedure TCustomCheckComboBoxEx.Clear;
begin
ClearItemStates;
inherited Clear;
end;
procedure TCustomCheckComboBoxEx.ClearItemStates;
var i: Integer;
begin
for i:=0 to Items.Count-1 do
begin
Items.Objects[i].Free;
Items.Objects[i]:=nil;
end;
end;
procedure TCustomCheckComboBoxEx.CloseUp;
begin
FDropped:=False;
if FRejectDropDown then
begin
FRejectDropDown:=False;
Update;
end else
inherited CloseUp;
end;
procedure TCustomCheckComboBoxEx.CMBiDiModeChanged(var Message: TLMessage);
begin
inherited CMBiDiModeChanged(Message);
FRightToLeft:=IsRightToLeft;
FNeedMeasure:=True;
Invalidate;
end;
procedure TCustomCheckComboBoxEx.DeleteItem(AIndex: Integer);
begin
if (AIndex>=0) and (AIndex<Items.Count) then
begin
Items.Objects[AIndex].Free;
Items.Delete(AIndex);
end;
end;
procedure TCustomCheckComboBoxEx.DrawItem(Index: Integer; ARect: TRect; State: TOwnerDrawState);
{ Enabled, State, Highlighted }
const caCheckThemes: array [Boolean, TCheckBoxState, Boolean] of TThemedButton =
{ normal, highlighted }
(((tbCheckBoxUncheckedDisabled, tbCheckBoxUncheckedDisabled), { disabled, unchecked }
(tbCheckBoxCheckedDisabled, tbCheckBoxCheckedDisabled), { disabled, checked }
(tbCheckBoxMixedDisabled, tbCheckBoxMixedDisabled)), { disabled, greyed }
((tbCheckBoxUncheckedNormal, tbCheckBoxUncheckedHot), { enabled, unchecked }
(tbCheckBoxCheckedNormal, tbCheckBoxCheckedHot), { enabled, checked }
(tbCheckBoxMixedNormal, tbCheckBoxMixedHot))); { enabled, greyed }
cCheckIndent: SmallInt = 2;
cTextIndent: SmallInt = 5;
var aDetail: TThemedElementDetails;
aDropped: Boolean;
aEnabled: Boolean;
aFlags: Cardinal;
aFocusedEditableMainItemNoDD: Boolean; { combo has edit-like line edit in csDropDownList (Win) and is closed (not DroppedDown }
aGray: Byte;
anyRect: TRect;
aState: TCheckBoxState;
ItemState: TCheckComboItemState;
begin { do not call inherited ! }
ItemState:=TCheckComboItemState(Items.Objects[Index]);
if not (ItemState is TCheckComboItemState) then
QueueCheckItemStates;
aDropped:=DroppedDown;
if aDropped and FRejectDropDown then
begin
DroppedDown:=False;
exit; { Exit! }
end;
aEnabled:=IsEnabled;
if not (csDesigning in ComponentState) then
aEnabled:= (aEnabled and ItemState.Enabled);
{$IF DEFINED(LCLWin32) or DEFINED(LCLWin64)}
aFocusedEditableMainItemNoDD := (Focused and (ARect.Left>0) and not aDropped);
{$ELSE}
aFocusedEditableMainItemNoDD := False;
{$ENDIF}
if (ARect.Left=0) or aFocusedEditableMainItemNoDD then
begin
if odSelected in State then
begin
if not aEnabled then
begin
aGray:=ColorToGray(Canvas.Brush.Color);
Canvas.Brush.Color:=RGBToColor(aGray, aGray, aGray);
end;
end else
Canvas.Brush.Color:=clWindow;
Canvas.Brush.Style:=bsSolid;
Canvas.FillRect(ARect);
end;
if not (csDesigning in ComponentState)
then aState:=ItemState.State
else aState:=cbUnchecked;
aDetail:=ThemeServices.GetElementDetails(caCheckThemes
[aEnabled, aState, not aDropped and FCheckHighlight]);
if FNeedMeasure then
begin
FCheckSize:=ThemeServices.GetDetailSize(aDetail);
FTextHeight:=Canvas.TextHeight('ŠjÁÇ');
if not aDropped then
begin
if not FRightToLeft then
begin
FHiLiteLeft:=-1;
FHiLiteRight:=ARect.Right;
end else
begin
FHiLiteLeft:=ARect.Left;
FHiLiteRight:=ARect.Right;
end;
FNeedMeasure := False;
end;
end;
if not FRightToLeft
then anyRect.Left:=ARect.Left+cCheckIndent
else anyRect.Left:=ARect.Right-cCheckIndent-FCheckSize.cx;
anyRect.Right:=anyRect.Left+FCheckSize.cx;
anyRect.Top:=(ARect.Bottom+ARect.Top-FCheckSize.cy) div 2;
anyRect.Bottom:=anyRect.Top+FCheckSize.cy;
ThemeServices.DrawElement(Canvas.Handle, aDetail, anyRect);
Canvas.Brush.Style:=bsClear;
if (not (odSelected in State) or not aDropped) and not aFocusedEditableMainItemNoDD
then Canvas.Font.Color:=clWindowText
else begin
Canvas.Font.Color:=clHighlightText;
FHilightedIndex:=Index;
end;
if aFocusedEditableMainItemNoDD then
begin
LCLIntf.SetBkColor(Canvas.Handle, ColorToRGB(clBtnFace));
LCLIntf.DrawFocusRect(Canvas.Handle, aRect);
end;
aFlags:=DT_END_ELLIPSIS+DT_VCENTER+DT_SINGLELINE+DT_NOPREFIX;
if not FRightToLeft then
begin
anyRect.Left:=ARect.Left+cCheckIndent+FCheckSize.cx+cTextIndent;
anyRect.Right:=ARect.Right;
end else
begin
anyRect.Right:=anyRect.Left-cTextIndent;
anyRect.Left:=ARect.Left;
aFlags:=aFlags or DT_RIGHT or DT_RTLREADING;
end;
anyRect.Top:=(ARect.Top+ARect.Bottom-FTextHeight) div 2;
anyRect.Bottom:=anyRect.Top+FTextHeight;
DrawText(Canvas.Handle, PChar(Items[Index]), Length(Items[Index]), anyRect, aFlags);
end;
procedure TCustomCheckComboBoxEx.DropDown;
{$IF DEFINED(LCLWin32) or DEFINED(LCLWin64)}
{$ELSE}
var aCursorPos: TPoint;
aRect: TRect;
{$ENDIF}
begin
{$IF DEFINED(LCLWin32) or DEFINED(LCLWin64)}
FRejectDropDown:=False;
{$ELSE}
aCursorPos:=ScreenToControl(Mouse.CursorPos);
aRect:=Rect(FHiLiteLeft, 0, FHiLiteRight, Height);
FRejectDropDown:=PtInRect(aRect, aCursorPos);
{$ENDIF}
FDropped:=True;
if not FRejectDropDown then
begin
inherited DropDown;
FRejectToggleOnSelect:=False;
end else
if (ItemIndex>=0) and ItemEnabled[ItemIndex] then Toggle(ItemIndex);
end;
procedure TCustomCheckComboBoxEx.FontChanged(Sender: TObject);
begin
FNeedMeasure:=True;
inherited FontChanged(Sender);
end;
procedure TCustomCheckComboBoxEx.InitializeWnd;
begin
InitItemStates;
inherited InitializeWnd;
CheckItemStates;
FRightToLeft:=IsRightToLeft;
end;
procedure TCustomCheckComboBoxEx.InitItemStates;
var i: Integer;
pItemState: TCheckComboItemState;
begin
for i:=0 to Items.Count-1 do
if Items.Objects[i]=nil then begin
pItemState:=TCheckComboItemState.Create;
pItemState.Enabled:=True;
pItemState.State:=cbUnchecked;
pItemState.Data:=nil;
Items.Objects[i]:=pItemState;
end else if not (Items.Objects[i] is TCheckComboItemState) then
raise Exception.Create(DbgSName(Self)+': Item '+IntToStr(i)+' is not a TCheckComboItemState');
end;
procedure TCustomCheckComboBoxEx.CheckItemStates;
var
i: Integer;
begin
for i:=0 to Items.Count-1 do
if not (Items.Objects[i] is TCheckComboItemState) then
raise Exception.Create(DbgSName(Self)+': Item '+IntToStr(i)+' is not a TCheckComboItemState');
end;
procedure TCustomCheckComboBoxEx.QueueCheckItemStates;
begin
Application.QueueAsyncCall(@AsyncCheckItemStates,0);
end;
procedure TCustomCheckComboBoxEx.KeyDown(var Key: Word; Shift: TShiftState);
begin
case Key of
VK_RETURN:
if FDropped then
if (ItemIndex=FHilightedIndex) and ItemEnabled[ItemIndex] then Toggle(ItemIndex);
VK_SPACE:
if DroppedDown then
if (ItemIndex>=0) and ItemEnabled[ItemIndex] then
begin
if ItemIndex<>FHilightedIndex then
begin
ItemIndex:=FHilightedIndex;
inherited Select;
end;
Toggle(ItemIndex);
DroppedDown:=False;
end;
end;
inherited KeyDown(Key, Shift);
end;
procedure TCustomCheckComboBoxEx.Loaded;
begin
inherited Loaded;
InitItemStates;
end;
procedure TCustomCheckComboBoxEx.MouseLeave;
begin
FCheckHighlight:=False;
inherited MouseLeave;
end;
procedure TCustomCheckComboBoxEx.MouseMove(Shift: TShiftState; X, Y: Integer);
var aHighlight: Boolean;
begin
inherited MouseMove(Shift, X, Y);
aHighlight:=((X>FHiLiteLeft) and (X<FHiLiteRight));
if aHighlight<>FCheckHighlight then
begin
FCheckHighlight:=aHighlight;
Invalidate;
end;
end;
procedure TCustomCheckComboBoxEx.Select;
begin
inherited Select;
{$IF DEFINED(LCLWin32) or DEFINED(LCLWin64)}
if DroppedDown then FRejectToggleOnSelect:=True;
{$ENDIF}
if not FRejectToggleOnSelect then
begin
if (ItemIndex >= 0) and ItemEnabled[ItemIndex] then Toggle(ItemIndex);
FRejectToggleOnSelect:=True;
end;
FDropped:=False;
end;
procedure TCustomCheckComboBoxEx.SetItemHeight(const AValue: Integer);
begin
inherited SetItemHeight(AValue);
FNeedMeasure:=True;
end;
procedure TCustomCheckComboBoxEx.SetItems(const Value: TStrings);
begin
ClearItemStates;
inherited SetItems(Value);
InitItemStates;
end;
procedure TCustomCheckComboBoxEx.Toggle(AIndex: Integer);
const caNewStateMap: array [TCheckBoxState, Boolean] of TCheckBoxState =
{ False (AllowGrayed) True }
((cbChecked, cbGrayed), { cbUnchecked }
(cbUnChecked, cbUnChecked), { cbChecked }
(cbChecked, cbChecked)); { cbGrayed }
begin
State[AIndex]:=caNewStateMap[State[AIndex], AllowGrayed];
end;
{ TCustomCheckCombo.Getters and Setters }
function TCustomCheckComboBoxEx.GetChecked(AIndex: Integer): Boolean;
begin
Result:=(TCheckComboItemState(Items.Objects[AIndex]).State=cbChecked);
end;
procedure TCustomCheckComboBoxEx.AsyncCheckItemStates(Data: PtrInt);
begin
CheckItemStates;
end;
function TCustomCheckComboBoxEx.GetCount: Integer;
begin
Result:=Items.Count;
end;
function TCustomCheckComboBoxEx.GetItemEnabled(AIndex: Integer): Boolean;
begin
Result:=TCheckComboItemState(Items.Objects[AIndex]).Enabled;
end;
function TCustomCheckComboBoxEx.GetObject(AIndex: Integer): TObject;
begin
Result:=TCheckComboItemState(Items.Objects[AIndex]).Data;
end;
function TCustomCheckComboBoxEx.GetState(AIndex: Integer): TCheckBoxState;
begin
Result:=TCheckComboItemState(Items.Objects[AIndex]).State;
end;
procedure TCustomCheckComboBoxEx.SetChecked(AIndex: Integer; AValue: Boolean);
begin
if AValue=(TCheckComboItemState(Items.Objects[AIndex]).State=cbChecked) then exit;
if AValue
then TCheckComboItemState(Items.Objects[AIndex]).State:=cbChecked
else TCheckComboItemState(Items.Objects[AIndex]).State:=cbUnchecked;
if Assigned(FOnItemChange) then
FOnItemChange(Self, AIndex);
if AIndex=ItemIndex then
Invalidate;
end;
procedure TCustomCheckComboBoxEx.SetItemEnabled(AIndex: Integer; AValue: Boolean);
begin
if TCheckComboItemState(Items.Objects[AIndex]).Enabled=AValue then exit;
TCheckComboItemState(Items.Objects[AIndex]).Enabled:=AValue;
if AIndex=ItemIndex then
Invalidate;
end;
procedure TCustomCheckComboBoxEx.SetObject(AIndex: Integer; AValue: TObject);
begin
TCheckComboItemState(Items.Objects[AIndex]).Data:=AValue;
end;
procedure TCustomCheckComboBoxEx.SetState(AIndex: Integer; AValue: TCheckBoxState);
begin
if TCheckComboItemState(Items.Objects[AIndex]).State=AValue then exit;
TCheckComboItemState(Items.Objects[AIndex]).State:=AValue;
if Assigned(FOnItemChange) then
FOnItemChange(self, AIndex);
if AIndex=ItemIndex then
Invalidate;
end;
end.