
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7943 8e941d3f-bd1b-0410-a28a-d453659cc2b4
476 lines
12 KiB
ObjectPascal
476 lines
12 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
|
|
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;
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
LCLType, LCLIntf;
|
|
|
|
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;
|
|
|
|
end.
|
|
|