lazarus-ccr/components/orpheus/ovcvlb.pas
macpgmr 32c8b1cb1c Patches for 64-bit support.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@2975 8e941d3f-bd1b-0410-a28a-d453659cc2b4
2014-04-28 20:45:29 +00:00

2541 lines
72 KiB
ObjectPascal

{*********************************************************}
{* OVCVLB.PAS 4.06 *}
{*********************************************************}
{* ***** BEGIN LICENSE BLOCK ***** *}
{* Version: MPL 1.1 *}
{* *}
{* The contents of this file are subject to the Mozilla Public License *}
{* Version 1.1 (the "License"); you may not use this file except in *}
{* compliance with the License. You may obtain a copy of the License at *}
{* http://www.mozilla.org/MPL/ *}
{* *}
{* Software distributed under the License is distributed on an "AS IS" basis, *}
{* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License *}
{* for the specific language governing rights and limitations under the *}
{* License. *}
{* *}
{* The Original Code is TurboPower Orpheus *}
{* *}
{* The Initial Developer of the Original Code is TurboPower Software *}
{* *}
{* Portions created by TurboPower Software Inc. are Copyright (C)1995-2002 *}
{* TurboPower Software Inc. All Rights Reserved. *}
{* *}
{* Contributor(s): *}
{* *}
{* ***** END LICENSE BLOCK ***** *}
{$I Ovc.INC}
{$B-} {Complete Boolean Evaluation}
{$I+} {Input/Output-Checking}
{$P+} {Open Parameters}
{$T-} {Typed @ Operator}
{.W-} {Windows Stack Frame}
{$X+} {Extended Syntax}
//{$Q-} {Arithmatic-Overflow Checking} <== Does this hide Turbopower bugs?
unit ovcvlb;
{-Virtual list box component}
interface
uses
{$IFNDEF LCL} Windows, Messages, {$ELSE} LclIntf, LMessages, Types, LclType, MyMisc, {$ENDIF}
Classes, Controls, Forms, Graphics, StdCtrls, Menus,
SysUtils, OvcBase, OvcData, OvcCmd, OvcConst, OvcMisc, OvcExcpt, OvcColor;
const
vlbMaxTabStops = 128; {maximum number of tab stops}
const
{default property values}
vlDefAutoRowHeight = True;
vlDefAlign = alNone;
vlDefBorderStyle = bsSingle;
vlDefColor = clWindow;
vlDefColumns = 255;
vlDefCtl3D = True;
vlDefHeaderBack = clBtnFace;
vlDefHeaderText = clBtnText;
vlDefHeight = 150;
vlDefIntegralHeight = True;
vlDefItemIndex = -1;
vlDefMultiSelect = False;
{$IFDEF MSWINDOWS}
vlDefNumItems = MaxLongInt; //2,147,483,647
{$ELSE}
vlDefNumItems = 126322582; //Apparent max. scrollbar positions with Carbon.
{$ENDIF} // GTK apparently allows 2,115,747,484.
vlDefOwnerDraw = False;
vlDefParentColor = False;
vlDefParentCtl3D = True;
vlDefParentFont = True;
vlDefProtectBack = clRed;
vlDefProtectText = clWhite;
vlDefRowHeight = 17;
vlDefScrollBars = ssVertical;
vlDefSelectBack = clHighlight;
vlDefSelectText = clHighlightText;
vlDefShowHeader = False;
vlDefTopIndex = 0;
vlDefTabStop = True;
vlDefUseTabStops = False;
vlDefWidth = 100;
type
TCharToItemEvent =
procedure(Sender : TObject; Ch : Char; var Index : LongInt)
of object;
{-event to notify caller of a key press and return new item index}
TDrawItemEvent =
procedure(Sender : TObject; Index : LongInt; Rect : TRect; const S : string)
of object;
{-event to allow user to draw the cell items}
TGetItemEvent =
procedure(Sender : TObject; Index : LongInt; var ItemString : string)
of object;
{-event to get string to display}
TGetItemColorEvent =
procedure(Sender : TObject; Index : LongInt; var FG, BG : TColor)
of object;
{-event to get color of the item cell}
TGetItemStatusEvent =
procedure(Sender : TObject; Index : LongInt; var Protect : Boolean)
of object;
{-event to get the protected status item cell}
THeaderClickEvent =
procedure(Sender : TObject; Point : TPoint)
of object;
{-event to notify of a mouse click in the header area}
TIsSelectedEvent =
procedure(Sender : TObject; Index : LongInt; var Selected : Boolean)
of object;
{-event to get the current selection status from the user}
TSelectEvent =
procedure(Sender : TObject; Index : LongInt; Selected : Boolean)
of object;
{-event to notify of a selection change}
TTopIndexChanged =
procedure(Sender : TObject; NewTopIndex : LongInt)
of object;
{-event to notify when the top index changes}
type
TTabStopArray = array[0..vlbMaxTabStops] of Integer;
TBuffer = array[0..255] of AnsiChar;
type
TOvcCustomVirtualListBox = class(TOvcCustomControlEx)
{.Z+}
protected {private}
{property variables}
FItemIndex : LongInt; {selected item}
FAutoRowHeight : Boolean; {true to handle row height calc}
FBorderStyle : TBorderStyle;{border style to use}
FColumns : Integer; {number of char columns}
FFillColor : TColor;
FHeader : string; {the column header}
FHeaderColor : TOvcColors; {header line colors}
FIntegralHeight : Boolean; {adjust height based on font}
FMultiSelect : Boolean; {allow multiple selections}
FNumItems : LongInt; {total number of items}
FOwnerDraw : Boolean; {true if user will draw rows}
FProtectColor : TOvcColors; {protected item colors}
FRowHeight : Integer; {height of one row}
FScrollBars : TScrollStyle;{scroll bar style to use}
FSelectColor : TOvcColors; {selected item color}
FShowHeader : Boolean; {true to use the header}
FSmoothScroll : Boolean; {use smooth scrolling (duh) }
FTopIndex : LongInt; {item at top of window}
FUseTabStops : Boolean; {true to use tab stops}
FWheelDelta : Integer;
{$IFDEF LCL}
FCtl3D : Boolean;
{$ENDIF}
{event variables}
FOnCharToItem : TCharToItemEvent;
FOnClickHeader : THeaderClickEvent;
FOnDrawItem : TDrawItemEvent;
FOnGetItem : TGetItemEvent;
FOnGetItemColor : TGetItemColorEvent;
FOnGetItemStatus : TGetItemStatusEvent;
FOnIsSelected : TIsSelectedEvent;
FOnSelect : TSelectEvent;
FOnTopIndexChanged : TTopIndexChanged;
FOnUserCommand : TUserCommandEvent;
{internal/working variables}
lAnchor : LongInt; {anchor point for extended selections}
lDivisor : LongInt; {divisor for scroll bars}
lDlgUnits : Integer; {used for tab spacing}
lFocusedIndex : LongInt; {index of the focused item}
lHaveHS : Boolean; {if True, we have a horizontal scroll bar}
lHaveVS : Boolean; {if True, we have a vertical scroll bar}
lHDelta : LongInt; {horizontal scroll delta}
lHighIndex : LongInt; {highest allowable index}
lNumTabStops : 0..vlbMaxTabStops; {number of tab stops in tabstop array}
lRows : Integer; {number of rows in window}
lString : TBuffer; {temp item string buffer}
lTabs : TTabStopArray;
lUpdating : Integer; {user updating flag}
lVSHigh : Integer; {vertical scroll limit}
lVMargin : Integer; {extra vertical line margin}
MousePassThru : Boolean;
{property methods}
procedure SetAutoRowHeight(Value : Boolean);
{-set use of auto row height calculations}
procedure SetBorderStyle(const Value : TBorderStyle);
{-set the style used for the border}
procedure SetColumns(const Value: Integer);
procedure SetHeader(const Value : string);
{-set the header at top of list box}
procedure SetIntegralHeight(Value : Boolean);
{-set use of integral font height adjustment}
procedure SetMultiSelect(Value : Boolean); virtual;
{-set ability to select multiple items}
procedure InternalSetNumItems(Value : LongInt; Paint, UpdateIndices : Boolean);
{-set the number of items in the list box}
procedure SetNumItems(Value : LongInt);
{-set the number of items in the list box}
procedure SetRowHeight(Value : Integer);
{-set height of cell row}
procedure SetScrollBars(const Value : TScrollStyle); virtual;
{-set use of vertical and horizontal scroll bars}
procedure SetShowHeader(Value : Boolean);
{-set the header at top of list box}
{internal methods}
procedure vlbAdjustIntegralHeight;
{-adjust height of the list box}
procedure vlbCalcFontFields; virtual;
{-calculate sizes based on font selection}
procedure vlbClearAllItems;
{-clear the highlight from all items}
procedure vlbClearSelRange(First, Last : LongInt);
{-clear the selection for the given range of indexes}
procedure vlbColorChanged(AColor: TObject);
{-a color has changed, refresh display}
procedure vlbDragSelection(First, Last : LongInt);
{-drag the selection}
procedure vlbDrawFocusRect(Index : LongInt);
{-draw the focus rectangle}
procedure vlbDrawHeader;
{-draw the header and text area}
procedure vlbExtendSelection(Index : LongInt);
{-process Shift-LMouseBtn}
procedure vlbHScrollPrim(Delta : Integer);
{-scroll horizontally}
procedure vlbInitScrollInfo;
{-setup scroll bar range and initial position}
procedure vlbMakeItemVisible(Index : LongInt);
{-make sure the item is visible}
procedure vlbNewActiveItem(Index : LongInt);
{-set the currently selected item}
function vlbScaleDown(N : LongInt) : Integer;
{-scale down index for scroll bar use}
function vlbScaleUp(N : LongInt) : LongInt;
{-scale up scroll index to our index}
procedure vlbSelectRangePrim(First, Last : LongInt; Select : Boolean);
{-change the selection for the given range of indexes}
procedure vlbSetAllItemsPrim(Select : Boolean);
{-primitive routine thats acts on all items}
procedure vlbSetFocusedIndex(Index : LongInt);
{-set focus to this item. invalidate previous}
procedure vlbSetHScrollPos;
{-set the horizontal scroll position}
procedure vlbSetHScrollRange;
{-set the horizontal scroll range}
procedure vlbSetSelRange(First, Last : LongInt);
{-set the selection on for the given range of indexes}
procedure vlbSetVScrollPos;
{-set the vertical scroll position}
procedure vlbSetVScrollRange;
{-set the vertical scroll range}
procedure vlbToggleSelection(Index : LongInt);
{-process Ctrl-LMouseBtn}
procedure vlbValidateItem(Index : LongInt);
{-validate the area for this item}
procedure vlbVScrollPrim(Delta : Integer);
{-scroll vertically}
{VCL control messages}
{$IFNDEF LCL}
procedure CMCtl3DChanged(var Message: TMessage);
message CM_CTL3DCHANGED;
{$ENDIF}
procedure CMFontChanged(var Message: TMessage);
message CM_FONTCHANGED;
{windows message methods}
procedure WMChar(var Msg : TWMChar);
message WM_CHAR;
procedure WMEraseBkgnd(var Msg : TWMEraseBkgnd);
message WM_ERASEBKGND;
procedure WMGetDlgCode(var Msg : TWMGetDlgCode);
message WM_GETDLGCODE;
procedure WMHScroll(var Msg : TWMScroll);
message WM_HSCROLL;
procedure WMKeyDown(var Msg : TWMKeyDown);
message WM_KEYDOWN;
procedure WMKillFocus(var Msg : TWMKillFocus);
message WM_KILLFOCUS;
procedure WMLButtonDown(var Msg : TWMLButtonDown);
message WM_LBUTTONDOWN;
procedure WMLButtonDblClk(var Msg : TWMLButtonDblClk);
message WM_LBUTTONDBLCLK;
procedure WMMouseActivate(var Msg : TWMMouseActivate);
message WM_MOUSEACTIVATE;
procedure WMSetFocus(var Msg : TWMSetFocus);
message WM_SETFOCUS;
procedure WMSize(var Msg : TWMSize);
message WM_SIZE;
procedure WMVScroll(var Msg : TWMScroll);
message WM_VSCROLL;
{list box messages}
procedure LBGetCaretIndex(var Msg : TMessage);
message LB_GETCARETINDEX;
procedure LBGetCount(var Msg : TMessage);
message LB_GETCOUNT;
procedure LBGetCurSel(var Msg : TMessage);
message LB_GETCURSEL;
procedure LBGetItemHeight(var Msg : TMessage);
message LB_GETITEMHEIGHT;
procedure LBGetItemRect(var Msg : TMessage);
message LB_GETITEMRECT;
procedure LBGetSel(var Msg : TMessage);
message LB_GETSEL;
procedure LBGetTopIndex(var Msg : TMessage);
message LB_GETTOPINDEX;
procedure LBResetContent(var Msg : TMessage);
message LB_RESETCONTENT;
procedure LBSelItemRange(var Msg : TMessage);
message LB_SELITEMRANGE;
procedure LBSetCurSel(var Msg : TMessage);
message LB_SETCURSEL;
procedure LBSetSel(var Msg : TMessage);
message LB_SETSEL;
procedure LBSetTabStops(var Msg : TMessage);
message LB_SETTABSTOPS;
procedure LBSetTopIndex(var Msg : TMessage);
message LB_SETTOPINDEX;
protected
procedure ChangeScale(M, D : Integer);
override;
procedure CreateParams(var Params: TCreateParams);
override;
procedure CreateWnd;
override;
procedure DragCanceled;
override;
procedure Paint;
override;
procedure WndProc(var Message: TMessage);
override;
{event wrappers}
function DoOnCharToItem(Ch : AnsiChar) : LongInt;
dynamic;
{-call the OnCharToItem event, if assigned}
procedure DoOnClickHeader(Point : TPoint);
dynamic;
{-call the OnClickHeader event, if assigned}
procedure DoOnDrawItem(Index : LongInt; Rect : TRect; const S : string);
virtual;
{-call the OnDrawItem event, if assigned}
function DoOnGetItem(Index : LongInt) : PAnsiChar;
virtual;
{-call the OnGetItem event, if assigned}
procedure DoOnGetItemColor(Index : LongInt; var FG, BG : TColor);
virtual;
{-call the OnGetItemColor event, if assigned}
function DoOnGetItemStatus(Index : LongInt) : Boolean;
virtual;
{-call the OnGetItemStatus event, if assigned}
function DoOnIsSelected(Index : LongInt) : Boolean;
virtual;
{-call the OnIsSelected event, if assigned}
procedure DoOnMouseWheel(Shift : TShiftState; Delta, XPos, YPos : SmallInt);
override;
procedure DoOnSelect(Index : LongInt; Selected : Boolean);
dynamic;
{-call the OnSelect event, if assigned}
procedure DoOnTopIndexChanged(NewTopIndex : LongInt);
dynamic;
{-call the OnTopIndexChanged event, if assigned}
procedure DoOnUserCommand(Command : Word);
dynamic;
{-perform notification of a user command}
{virtual property methods}
procedure SetItemIndex(Index : LongInt);
virtual;
{-change the currently selected item}
procedure SetTopIndex(Index : LongInt);
virtual;
{-set the index of the first visible entry in the list}
procedure ForceTopIndex(Index : LongInt; ThumbTracking : Boolean);
virtual;
{-re-set the index of the first visible entry in the list - even if it doesn't change}
procedure SimulatedClick;
virtual;
{-generates a click event when called. Called from SetItemIndex. Introduced so that
descendants can turn off the behavior.}
function IsValidIndex(Index : LongInt) : Boolean;
{.Z-}
{protected properties}
property AutoRowHeight : Boolean
read FAutoRowHeight write SetAutoRowHeight default vlDefAutoRowHeight;
property BorderStyle : TBorderStyle
read FBorderStyle write SetBorderStyle default vlDefBorderStyle;
property Columns : Integer
read FColumns write SetColumns default vlDefColumns;
property Header : string
read FHeader write SetHeader;
property HeaderColor : TOvcColors
read FHeaderColor write FHeaderColor;
property IntegralHeight : Boolean
read FIntegralHeight write SetIntegralHeight default vlDefIntegralHeight;
property MultiSelect : Boolean
read FMultiSelect write SetMultiSelect default vlDefMultiSelect;
property NumItems : LongInt
read FNumItems write SetNumItems default vlDefNumItems;
property OwnerDraw : Boolean
read FOwnerDraw write FOwnerDraw default vlDefOwnerDraw;
property ProtectColor : TOvcColors
read FProtectColor write FProtectColor;
property RowHeight : Integer
read FRowHeight write SetRowHeight default vlDefRowHeight;
property ScrollBars : TScrollStyle
read FScrollBars write SetScrollBars default vlDefScrollBars;
property SelectColor : TOvcColors
read FSelectColor write FSelectColor;
property ShowHeader : Boolean
read FShowHeader write SetShowHeader default vlDefShowHeader;
property UseTabStops : Boolean
read FUseTabStops write FUseTabStops default vlDefUseTabStops;
property WheelDelta: Integer
read FWheelDelta write FWheelDelta default 3;
{$IFDEF LCL}
property Ctl3D : Boolean read FCtl3D write FCtl3D default vlDefCtl3D;
{$ENDIF}
{protected events}
property OnCharToItem : TCharToItemEvent
read FOnCharToItem write FOnCharToItem;
property OnClickHeader : THeaderClickEvent
read FOnClickHeader write FOnClickHeader;
property OnDrawItem : TDrawItemEvent
read FOnDrawItem write FOnDrawItem;
property OnGetItem : TGetItemEvent
read FOnGetItem write FOnGetItem;
property OnGetItemColor : TGetItemColorEvent
read FOnGetItemColor write FOnGetItemColor;
property OnGetItemStatus : TGetItemStatusEvent
read FOnGetItemStatus write FOnGetItemStatus;
property OnIsSelected : TIsSelectedEvent
read FOnIsSelected write FOnIsSelected;
property OnSelect : TSelectEvent
read FOnSelect write FOnSelect;
property OnTopIndexChanged : TTopIndexChanged
read FOnTopIndexChanged write FOnTopIndexChanged;
property OnUserCommand : TUserCommandEvent
read FOnUserCommand write FOnUserCommand;
public
{.Z+}
constructor Create(AOwner : TComponent);
override;
destructor Destroy;
override;
{.Z-}
procedure BeginUpdate; virtual;
{-user is updating the list items--don't paint}
procedure CenterCurrentLine;
{- center the currently selected line (if any) on screen}
procedure CenterLine(Index : Integer);
{- center the specified line (if any) vertically on screen}
procedure DeselectAll;
{-deselect all items}
procedure DrawItem(Index : LongInt);
{-invalidate and update the area for this item}
procedure EndUpdate; virtual;
{-user is done updating the list items--force repaint}
procedure InsertItemsAt(Items : LongInt; Index : LongInt);
{-increase NumItems with Items amount while scrolling window down from Index}
procedure DeleteItemsAt(Items : LongInt; Index : LongInt);
{-decrease NumItems with Items amount while scrolling window up from Index}
procedure InvalidateItem(Index : LongInt);
{-invalidate the area for this item}
function ItemAtPos(Pos : TPoint; Existing : Boolean) : LongInt;
{-return the index of the cell that contains the point Pos}
procedure Scroll(HDelta, VDelta : Integer);
{-scroll the list by the give delta amount}
procedure SelectAll;
{-select all items}
procedure SetBounds(ALeft, ATop, AWidth, AHeight : Integer);
override;
procedure SetTabStops(const Tabs : array of Integer);
{-set tab stop positions}
{public properties}
property Canvas;
property ItemIndex : LongInt
read FItemIndex write SetItemIndex;
property FillColor : TColor read FFillColor write FFillColor;
property SmoothScroll : Boolean
read FSmoothScroll write FSmoothScroll default True;
property TopIndex : LongInt
read FTopIndex write SetTopIndex;
end;
TOvcVirtualListBox = class(TOvcCustomVirtualListBox)
published
property AutoRowHeight;
property BorderStyle;
property Columns;
property Header;
property HeaderColor;
property IntegralHeight;
property MultiSelect;
property NumItems;
property OwnerDraw;
property ProtectColor;
property RowHeight;
property ScrollBars;
property SelectColor;
property ShowHeader;
property SmoothScroll;
property UseTabStops;
property WheelDelta;
property OnCharToItem;
property OnClickHeader;
property OnDrawItem;
property OnGetItem;
property OnGetItemColor;
property OnGetItemStatus;
property OnIsSelected;
property OnSelect;
property OnTopIndexChanged;
property OnUserCommand;
{inherited properties}
{$IFDEF VERSION4}
property Anchors;
property Constraints;
property DragKind;
{$ENDIF}
property Align;
property Color;
property Controller;
property Ctl3D;
property DragCursor;
property DragMode;
property Enabled;
property Font;
property ParentColor default vlDefParentColor;
{$IFNDEF LCL}
property ParentCtl3D default vlDefParentCtl3D;
{$ENDIF}
property ParentFont default vlDefParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property TabOrder;
property TabStop default vlDefTabStop;
property Visible;
{inherited events}
property AfterEnter;
property AfterExit;
property OnClick;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnStartDrag;
end;
implementation
{const
vlbWheelDelta = 3;} {changed to property}
{*** TOvcVirtualListBox ***}
procedure TOvcCustomVirtualListBox.BeginUpdate;
{-user is updating the list items--don't paint}
begin
inc(lUpdating);
end;
procedure TOvcCustomVirtualListBox.CenterCurrentLine;
{- center the currently selected line (if any) on screen}
begin
if ItemIndex <> -1 then
TopIndex := ItemIndex - (lRows div 2);
end;
procedure TOvcCustomVirtualListBox.CenterLine(Index : Integer);
begin
if Index <> -1 then
TopIndex := Index - (lRows div 2);
end;
procedure TOvcCustomVirtualListBox.ChangeScale(M, D : Integer);
begin
inherited ChangeScale(M, D);
if M <> D then begin
{scale row height}
FRowHeight := MulDiv(FRowHeight, M, D);
vlbCalcFontFields;
vlbAdjustIntegralHeight;
vlbCalcFontFields;
vlbInitScrollInfo;
Refresh;
end;
end;
{$IFNDEF LCL}
procedure TOvcCustomVirtualListBox.CMCtl3DChanged(var Message: TMessage);
begin
if (csLoading in ComponentState) or not HandleAllocated then
Exit;
if NewStyleControls and (FBorderStyle = bsSingle) then
{$IFNDEF LCL}
RecreateWnd;
{$ELSE}
MyMisc.RecreateWnd(Self);
{$ENDIF}
inherited;
end;
{$ENDIF}
procedure TOvcCustomVirtualListBox.CMFontChanged(var Message: TMessage);
begin
inherited;
if (csLoading in ComponentState) then
Exit;
if not HandleAllocated then
Exit;
{reset internal size variables}
if FIntegralHeight then begin
vlbCalcFontFields;
vlbAdjustIntegralHeight;
end;
vlbCalcFontFields;
vlbInitScrollInfo;
end;
constructor TOvcCustomVirtualListBox.Create(AOwner : TComponent);
begin
inherited Create(AOwner);
FillColor := Color;
FSmoothScroll := True;
if NewStyleControls then
ControlStyle := ControlStyle + [csClickEvents, csCaptureMouse, csOpaque]
else
ControlStyle := ControlStyle + [csClickEvents, csCaptureMouse, csOpaque, csFramed];
{set default values for inherited persistent properties}
Align := vlDefAlign;
Color := vlDefColor;
Ctl3D := vlDefCtl3D;
Height := vlDefHeight;
ParentColor := vlDefParentColor;
{$IFNDEF LCL}
ParentCtl3D := vlDefParentCtl3D;
{$ENDIF}
ParentFont := vlDefParentFont;
TabStop := vlDefTabStop;
Width := vlDefWidth;
{set default values for new persistent properties}
FAutoRowHeight := vlDefAutoRowHeight;
FBorderStyle := vlDefBorderStyle;
FColumns := vlDefColumns;
FHeader := '';
FIntegralHeight := vlDefIntegralHeight;
FItemIndex := vlDefItemIndex;
FMultiSelect := vlDefMultiSelect;
FNumItems := vlDefNumItems;
FOwnerDraw := vlDefOwnerDraw;
FRowHeight := vlDefRowHeight;
FScrollBars := vlDefScrollBars;
FShowHeader := vlDefShowHeader;
FTopIndex := vlDefTopIndex;
FUseTabStops := vlDefUseTabStops;
{set defaults for internal variables}
lHDelta := 0;
lHaveHS := False;
lHaveVS := False;
lAnchor := 0;
lFocusedIndex := 0; {-1;}
lNumTabStops := 0;
FillChar(lTabs, SizeOf(lTabs), #0);
{create and initialize color objects}
FHeaderColor := TOvcColors.Create(vlDefHeaderText, vlDefHeaderBack);
FHeaderColor.OnColorChange := vlbColorChanged;
FProtectColor := TOvcColors.Create(vlDefProtectText, vlDefProtectBack);
FProtectColor.OnColorChange := vlbColorChanged;
FSelectColor := TOvcColors.Create(vlDefSelectText, vlDefSelectBack);
FSelectColor.OnColorChange := vlbColorChanged;
FWheelDelta := 3;
end;
procedure TOvcCustomVirtualListBox.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do
Style := Style or DWord(ScrollBarStyles[FScrollBars])
or DWord(BorderStyles[FBorderStyle]);
if NewStyleControls and Ctl3D and (FBorderStyle = bsSingle) then begin
Params.Style := Params.Style and not WS_BORDER;
Params.ExStyle := Params.ExStyle or WS_EX_CLIENTEDGE;
end;
{$IFDEF LCL}
if not (csDesigning in ComponentState) then
inherited SetBorderStyle(FBorderStyle); //Crashes IDE for some reason
{$ENDIF}
end;
procedure TOvcCustomVirtualListBox.CreateWnd;
begin
inherited CreateWnd;
{do we have scroll bars}
lHaveVS := FScrollBars in [ssVertical, ssBoth];
lHaveHS := FScrollBars in [ssHorizontal, ssBoth];
lHighIndex := Pred(FNumItems);
lFocusedIndex := 0; {-1;}
{determine the height of one row and number of rows}
vlbCalcFontFields;
vlbAdjustIntegralHeight;
{setup scroll bar info}
vlbInitScrollInfo;
end;
function TOvcCustomVirtualListBox.DoOnCharToItem(Ch : AnsiChar) : LongInt;
begin
Result := FItemIndex;
if Assigned(FOnCharToItem) then
FOnCharToItem(Self, Ch, Result);
end;
procedure TOvcCustomVirtualListBox.DoOnClickHeader(Point : TPoint);
begin
if Assigned(FOnClickHeader) then
FOnClickHeader(Self, Point);
end;
procedure TOvcCustomVirtualListBox.DoOnDrawItem(Index : LongInt; Rect : TRect;
const S : string);
begin
if Assigned(FOnDrawItem) then
FOnDrawItem(Self, Index, Rect, S);
end;
function TOvcCustomVirtualListBox.DoOnGetItem(Index : LongInt) : PAnsiChar;
{-returns the string representing Nth item}
var
S : string;
begin
if Assigned(FOnGetItem) {$IFDEF LCL} and not (csDesigning in ComponentState) {$ENDIF} then begin
S := '';
FOnGetItem(Self, Index, S);
StrPCopy(lString, S);
Result := @lString[0];
end else if csDesigning in ComponentState then begin
StrPCopy(lString, Format(GetOrphStr(SCSampleListItem), [Index]));
Result := @lString[0];
end else
Result := StrPCopy(lString, Format(GetOrphStr(SCGotItemWarning), [Index]));
end;
procedure TOvcCustomVirtualListBox.DoOnGetItemColor(Index : LongInt; var FG, BG : TColor);
begin
if Assigned(FOnGetItemColor) then
FOnGetItemColor(Self, Index, FG, BG);
end;
function TOvcCustomVirtualListBox.DoOnGetItemStatus(Index : LongInt) : Boolean;
begin
Result := False;
if Assigned(FOnGetItemStatus) then
FOnGetItemStatus(Self, Index, Result);
end;
function TOvcCustomVirtualListBox.DoOnIsSelected(Index : LongInt) : Boolean;
{-returns the selected status for the "Index" item}
begin
if csDesigning in ComponentState then
Result := Index = 0
else begin
Result := (Index = FItemIndex);
if FMultiSelect then begin
if Assigned(FOnIsSelected) then
FOnIsSelected(Self, Index, Result)
else
raise EOnIsSelectedNotAssigned.Create;
end;
end;
end;
procedure TOvcCustomVirtualListBox.DoOnMouseWheel(Shift : TShiftState; Delta, XPos, YPos : SmallInt);
var
I : Integer;
begin
inherited DoOnMouseWheel(Shift, Delta, XPos, YPos);
if Delta < 0 then begin
for I := 1 to {vlb}WheelDelta do
Perform(WM_VSCROLL, MAKELONG(SB_LINEDOWN, 0), 0);
end else if Delta > 0 then begin
for I := 1 to {vlb}WheelDelta do
Perform(WM_VSCROLL, MAKELONG(SB_LINEUP, 0), 0);
end;
end;
procedure TOvcCustomVirtualListBox.DoOnSelect(Index : LongInt; Selected : Boolean);
{-notify of selection change}
begin
if csDesigning in ComponentState then
Exit;
if FMultiSelect then begin
if Assigned(FOnSelect) then begin
{select if not protected-deselect always}
if (not Selected) or (not DoOnGetItemStatus(Index)) then
FOnSelect(Self, Index, Selected);
end else
raise EOnSelectNotAssigned.Create;
end;
end;
procedure TOvcCustomVirtualListBox.DoOnTopIndexChanged(NewTopIndex : LongInt);
{-call the OnTopIndexChanged event, if assigned}
begin
if Assigned(FOnTopIndexChanged) then
FOnTopIndexChanged(Self, NewTopIndex);
end;
procedure TOvcCustomVirtualListBox.DoOnUserCommand(Command : Word);
{-perform notification of a user command}
begin
if Assigned(FOnUserCommand) then
FOnUserCommand(Self, Command);
end;
procedure TOvcCustomVirtualListBox.DeselectAll;
{-deselect all items}
begin
vlbSetAllItemsPrim(False {deselect});
end;
procedure TOvcCustomVirtualListBox.DrawItem(Index : LongInt);
{-invalidate and update the area for this item}
begin
InvalidateItem(Index);
Update;
end;
destructor TOvcCustomVirtualListBox.Destroy;
begin
{if lUpdating <> 0 then debug code}
{raise Exception.Create('Mismatched BeginUpdate/EndUpdate');}
FHeaderColor.Free;
FProtectColor.Free;
FSelectColor.Free;
inherited Destroy;
end;
procedure TOvcCustomVirtualListBox.EndUpdate;
{-user is done updating the list items--force repaint}
begin
dec(lUpdating);
if lUpdating < 0 then
raise Exception.Create('Mismatched BeginUpdate/EndUpdate');
if lUpdating = 0 then
Invalidate;
end;
function ScrollCanvas(Canvas : TCanvas; R : TRect; EastWest : Boolean; Distance : Integer;
Smooth : Boolean) : TRect;
var
UpdRect : TRect;
NextStep,StepSize : Integer;
{OldColor : TColor;}
begin
if Distance = 0 then begin
Result := Rect(0,0,0,0);
exit;
end;
if Smooth then
StepSize := MaxI((Abs(Distance) div 4), MinI(2, Abs(Distance)))
else
StepSize := Abs(Distance);
Result := R;
if EastWest then
if abs(Distance) < (Result.Right-Result.Left+1) then
if Distance < 0 then
Result.Left := Result.Right + Distance
else
Result.Right := Result.Left + Distance
else
else
if abs(Distance) < (Result.Bottom-Result.Top+1) then
if Distance < 0 then
Result.Top := Result.Bottom + Distance
else
Result.Bottom := Result.Top + Distance;
repeat
if Distance > 0 then
if Distance > StepSize then
NextStep := StepSize
else
NextStep := Distance
else
if Distance < -StepSize then
NextStep := -StepSize
else
NextStep := Distance;
if EastWest then
ScrollDC(Canvas.Handle,NextStep,0,R,R,0,@UpdRect)
else
ScrollDC(Canvas.Handle,0,NextStep,R,R,0,@UpdRect);
UnionRect(Result, UpdRect, Result);
dec(Distance,NextStep);
until Distance = 0;
end;
procedure TOvcCustomVirtualListBox.InsertItemsAt(Items : LongInt; Index : LongInt);
{-increase NumItems with Items amount while scrolling window down from Index}
var
CR : TRect;
AbsBottom : Integer;
OldItemIndex : Integer;
begin
OldItemIndex := ItemIndex;
ItemIndex := -1;
InternalSetNumItems(NumItems + Items,False,False);
if (lUpdating = 0) then
if (Index-FTopIndex) < lRows then begin
AbsBottom := (ClientRect.Bottom div FRowHeight) * FRowHeight;
if Index >= FTopIndex then begin
CR := Rect(0, (Index-FTopIndex+Ord(FShowHeader))*FRowHeight, ClientWidth, AbsBottom);
{Make sure the canvas is updated,
because we will be validating the scrolled portion.}
CR := ScrollCanvas(Canvas, CR, False, Items*FRowHeight, FSmoothScroll);
InvalidateRect(Handle,@CR,False);
end else begin
CR := Rect(0, (Ord(FShowHeader))*FRowHeight, ClientWidth, AbsBottom);
Update;
{Make sure the canvas is updated,
because we will be validating the scrolled portion.}
CR := ScrollCanvas(Canvas, CR, False, Items*FRowHeight, FSmoothScroll);
InvalidateRect(Handle, @CR, False);
Update;
end;
end;
if OldItemIndex >= Index then
inc(OldItemIndex,Items);
ItemIndex := OldItemIndex;
end;
procedure TOvcCustomVirtualListBox.DeleteItemsAt(Items : LongInt; Index : LongInt);
{-decrease NumItems with Items amount while scrolling window up from Index}
var
CR : TRect;
AbsBottom,OldItemIndex : Integer;
begin
OldItemIndex := ItemIndex;
ItemIndex := -1;
if lUpdating = 0 then
Update;
InternalSetNumItems(NumItems - Items,False,False);
if lUpdating = 0 then begin
if (Index-FTopIndex) < lRows then begin
AbsBottom := (ClientRect.Bottom div FRowHeight) * FRowHeight;
if Index >= FTopIndex then begin
CR := Rect(0, (Index-FTopIndex+Ord(FShowHeader))*FRowHeight, ClientWidth, AbsBottom);
CR := ScrollCanvas(Canvas, CR, False, -Items*FRowHeight, FSmoothScroll);
InvalidateRect(Handle,@CR,False);
Update;
end else begin
CR := Rect(0, (Ord(FShowHeader))*FRowHeight, ClientWidth, AbsBottom);
CR := ScrollCanvas(Canvas, CR, False, -Items*FRowHeight, FSmoothScroll);
InvalidateRect(Handle,@CR,False);
Update;
end;
end;
end;
if OldItemIndex >= Index+Items then
dec(OldItemIndex,Items)
else
if OldItemIndex >= Index then
OldItemIndex := -1;
ItemIndex := OldItemIndex;
if TopIndex + lRows > FNumItems then
ForceTopIndex(FNumItems - 1, True)
else
ForceTopIndex(TopIndex, False);
end;
procedure TOvcCustomVirtualListBox.InvalidateItem(Index : LongInt);
{-invalidate the area for this item}
var
CR : TRect;
begin
if (Index >= FTopIndex) and (Index-FTopIndex < lRows) then begin {visible?}
CR := Rect(0, (Index-FTopIndex+Ord(FShowHeader))*FRowHeight, ClientWidth, 0);
CR.Bottom := CR.Top+FRowHeight;
InvalidateRect(Handle, @CR, True);
end;
end;
function TOvcCustomVirtualListBox.ItemAtPos(Pos : TPoint;
Existing : Boolean) : LongInt;
{-return the index of the cell that contains the point Pos}
begin
if (Pos.Y < Ord(FShowHeader)*FRowHeight) then begin
if Existing then
Result := -1
else
Result := 0;
end else if (Pos.Y >= ClientHeight) then begin
if Existing then
Result := -1
else
Result := lHighIndex;
end else begin {convert to an index}
Result := FTopIndex-Ord(FShowHeader)+(Pos.Y div FRowHeight);
{test for click below last item (IntegralHeight not set)}
if ClientHeight mod FRowHeight > 0 then
if Result > FTopIndex+lRows-1 then
Result := FTopIndex+lRows-1;
if Result > NumItems then
if Existing then
Result := -1
else
Result := NumItems;
end;
end;
procedure TOvcCustomVirtualListBox.LBGetCaretIndex(var Msg : TMessage);
begin
Msg.Result := lFocusedIndex;
end;
procedure TOvcCustomVirtualListBox.LBGetCount(var Msg : TMessage);
begin
Msg.Result := FNumItems;
end;
procedure TOvcCustomVirtualListBox.LBGetCurSel(var Msg : TMessage);
begin
Msg.Result := FItemIndex;
end;
procedure TOvcCustomVirtualListBox.LBGetItemHeight(var Msg : TMessage);
begin
Msg.Result := FRowHeight;
end;
procedure TOvcCustomVirtualListBox.LBGetItemRect(var Msg : TMessage);
begin
PRect(Msg.LParam)^ :=
Rect(0, (Msg.WParam - FTopIndex) * FRowHeight,
ClientWidth, (Msg.WParam - FTopIndex) * FRowHeight + FRowHeight);
end;
procedure TOvcCustomVirtualListBox.LBGetSel(var Msg : TMessage);
begin
if (Msg.wParam >= 0) and (Msg.wParam <= lHighIndex) then
Msg.Result := Ord(DoOnIsSelected(Msg.wParam))
else
Msg.Result := LB_ERR;
end;
procedure TOvcCustomVirtualListBox.LBGetTopIndex(var Msg : TMessage);
begin
Msg.Result := FTopIndex;
end;
procedure TOvcCustomVirtualListBox.LBResetContent(var Msg : TMessage);
begin
NumItems := 0;
end;
procedure TOvcCustomVirtualListBox.LBSelItemRange(var Msg : TMessage);
begin
if FMultiSelect and (LoWord(Msg.WParam) <= lHighIndex) //64
and (HiWord(Msg.WParam) <= lHighIndex) then begin //64
vlbSelectRangePrim(LoWord(Msg.LParam), HiWord(Msg.LParam), Msg.wParam > 0); //64
Msg.Result := 0;
end else
Msg.Result := LB_ERR;
end;
procedure TOvcCustomVirtualListBox.LBSetCurSel(var Msg : TMessage);
begin
if FMultiSelect and (Msg.wParam >= -1) and (Msg.wParam <= lHighIndex) then begin
SetItemIndex(Msg.wParam);
if Msg.wParam = $FFFF then
Msg.Result := LB_ERR
else
Msg.Result := 0;
end else
Msg.Result := LB_ERR;
end;
procedure TOvcCustomVirtualListBox.LBSetSel(var Msg : TMessage);
begin
if FMultiSelect and (Msg.lParam >= -1) and (Msg.lParam <= lHighIndex) then begin
if Msg.lParam = -1 then
vlbSetAllItemsPrim(Msg.wParam > 0)
else begin
DoOnSelect(Msg.lParam, Msg.wParam > 0);
InvalidateItem(Msg.lParam);
end;
Msg.Result := 0;
end else
Msg.Result := LB_ERR;
end;
procedure TOvcCustomVirtualListBox.LBSetTabStops(var Msg : TMessage);
type
IA = TTabStopArray;
IP = ^IA;
var
I : Integer;
begin
lNumTabStops := Msg.wParam;
if lNumTabStops > vlbMaxTabStops then begin
lNumTabStops := vlbMaxTabStops;
Msg.Result := 0; {didn't set all tabs}
end else
Msg.Result := 1;
for I := 0 to Pred(lNumTabStops) do
lTabs[I] := IP(Msg.lParam)^[I] * lDlgUnits;
end;
procedure TOvcCustomVirtualListBox.LBSetTopIndex(var Msg : TMessage);
begin
if (Msg.wParam >= 0) and (Msg.wParam <= lHighIndex) then begin
SetTopIndex(Msg.wParam);
Msg.Result := 0;
end else
Msg.Result := LB_ERR;
end;
procedure TOvcCustomVirtualListBox.Paint;
var
I : Integer;
ST : PAnsiChar;
CR : TRect;
IR : TRect;
Clip : TRect;
Last : Integer;
{20070204 workaround for recent change to FPC that
no longer permits nested procedure to have same
name as other method in class.}
{$IFNDEF FPC}
procedure DrawItem(N : LongInt; Row : Integer);
{$ELSE}
procedure DrawItem2(N : LongInt; Row : Integer);
{$ENDIF}
{-Draw item N at Row}
var
S : PAnsiChar;
FGColor : TColor;
BGColor : TColor;
DX : Integer;
begin
{get bounding rectangle}
CR.Top := Pred(Row)*FRowHeight;
CR.Bottom := CR.Top+FRowHeight;
{do we have anything to paint}
if Bool(IntersectRect(IR, Clip, CR)) then begin
{get colors}
if DoOnGetItemStatus(N) then begin
BGColor := FProtectColor.BackColor;
FGColor := FProtectColor.TextColor;
end else if DoOnIsSelected(N) and (Row <= lRows+Ord(FShowHeader)) then begin
BGColor := FSelectColor.BackColor;
FGColor := FSelectColor.TextColor;
end else begin
BGColor := Color;
FGColor := Font.Color;
DoOnGetItemColor(N, FGColor, BGColor);
end;
{assign colors to our canvas}
Canvas.Brush.Color := BGColor;
Canvas.Font.Color := FGColor;
{clear the line}
Canvas.FillRect(CR);
{get the string}
if N <= lHighIndex then begin
ST := DoOnGetItem(N);
if lHDelta >= LongInt(StrLen(ST)) then
S := nil
else
S := @ST[lHDelta];
end else
S := nil;
{draw the string}
if S <> nil then begin
if FOwnerDraw then
DoOnDrawItem(N, CR, StrPas(S))
else if FUseTabStops then begin
DX := 0;
if lHDelta > 0 then begin
{measure portion of string to the left of the window}
DX := LOWORD(GetTabbedTextExtent(Canvas.Handle,
ST, lHDelta, lNumTabStops, lTabs));
end;
TabbedTextOut(Canvas.Handle, CR.Left+2, CR.Top,
S, StrLen(S), lNumTabStops, lTabs, -DX)
end else
ExtTextOut(Canvas.Handle, CR.Left+2, CR.Top,
ETO_CLIPPED + ETO_OPAQUE, @CR, S, StrLen(S), nil);
end;
end;
end;
begin
{exit if the updating flag is set}
if lUpdating > 0 then
Exit;
Canvas.Font := Font;
{we will erase our own background}
SetBkMode(Canvas.Handle, TRANSPARENT);
{get the client rectangle}
CR := ClientRect;
{get the clipping region}
{$IFNDEF LCL}
GetClipBox(Canvas.Handle, Clip);
{$ELSE}
GetClipBox(Canvas.Handle, @Clip);
{$ENDIF}
{do we have a header?}
if FShowHeader then begin
if Bool(IntersectRect(IR, Clip, Rect(CR.Left, CR.Top, CR.Right, FRowHeight))) then
vlbDrawHeader;
end;
{calculate last visible item}
Last := lRows;
if Last > NumItems then
Last := NumItems;
{display each row}
for I := 1 to Last do
{$IFNDEF FPC}
DrawItem(FTopIndex+Pred(I), I+Ord(FShowHeader));
{$ELSE}
DrawItem2(FTopIndex+Pred(I), I+Ord(FShowHeader));
{$ENDIF}
{paint any blank area below last item}
CR.Top := FRowHeight * (Last+Ord(FShowHeader));
if CR.Top < ClientHeight then begin
CR.Bottom := ClientHeight;
{clear the area}
Canvas.Brush.Color := Color;
Canvas.FillRect(CR);
end;
Canvas.Brush.Color := Color;
Canvas.Font.Color := Font.Color;
if Canvas.Handle > 0 then {force colors to be selected into canvas};
{conditionally, draw the focus rect}
if lFocusedIndex <> -1 then
vlbDrawFocusRect(lFocusedIndex);
end;
procedure TOvcCustomVirtualListBox.DragCanceled;
var
M: TWMMouse;
P, MousePos: TPoint;
begin
with M do
begin
Msg := WM_LBUTTONDOWN;
GetCursorPos(MousePos);
P := ScreenToClient(MousePos);
Pos := PointToSmallPoint(P);
Keys := 0;
Result := 0;
end;
DefaultHandler(M);
M.Msg := WM_LBUTTONUP;
DefaultHandler(M);
end;
procedure TOvcCustomVirtualListBox.WndProc(var Message: TMessage);
begin
{for auto drag mode, let listbox handle itself, instead of TControl}
if not (csDesigning in ComponentState) and ((Message.Msg = WM_LBUTTONDOWN) or
(Message.Msg = WM_LBUTTONDBLCLK)) and not Dragging then
begin
if DragMode = dmAutomatic then
begin
if IsControlMouseMsg(TWMMouse(Message)) then
Exit;
ControlState := ControlState + [csLButtonDown];
Dispatch(Message); {overrides TControl's BeginDrag}
Exit;
end;
end;
inherited WndProc(Message);
end;
procedure TOvcCustomVirtualListBox.Scroll(HDelta, VDelta : Integer);
{-scroll the list by the give delta amount}
begin
if HDelta <> 0 then
vlbHScrollPrim(HDelta);
if VDelta <> 0 then
vlbVScrollPrim(VDelta);
end;
procedure TOvcCustomVirtualListBox.SelectAll;
{-select all items}
begin
vlbSetAllItemsPrim(True {select});
end;
procedure TOvcCustomVirtualListBox.SetAutoRowHeight(Value : Boolean);
{-set use of auto row height calculations}
begin
if Value <> FAutoRowHeight then begin
FAutoRowHeight := Value;
if FAutoRowHeight then begin
vlbCalcFontFields;
vlbAdjustIntegralHeight;
vlbCalcFontFields;
vlbInitScrollInfo;
Refresh;
end;
end;
end;
procedure TOvcCustomVirtualListBox.SetBorderStyle(const Value : TBorderStyle);
{-set the style used for the border}
begin
if Value <> FBorderStyle then begin
FBorderStyle := Value;
{$IFNDEF LCL}
RecreateWnd;
{$ELSE}
MyMisc.RecreateWnd(Self);
{$ENDIF}
end;
end;
procedure TOvcCustomVirtualListBox.SetBounds(ALeft, ATop, AWidth, AHeight : Integer);
begin
if not (Align in [alNone, alTop, alBottom]) then
FIntegralHeight := False;
inherited SetBounds(ALeft, ATop, AWidth, AHeight);
end;
procedure TOvcCustomVirtualListBox.SetHeader(const Value : string);
{-set the header at top of list box}
begin
if Value <> FHeader then begin
FHeader := Value;
{toggle show header flag as appropriate}
if (csDesigning in ComponentState) and not (csLoading in ComponentState) then
ShowHeader := FHeader <> '';
Repaint;
end;
end;
procedure TOvcCustomVirtualListBox.SetIntegralHeight(Value : Boolean);
{-set use of integral font height adjustment}
begin
if (Value <> FIntegralHeight) and (Align in [alNone, alTop, alBottom]) then begin
FIntegralHeight := Value;
if FIntegralHeight then begin
vlbCalcFontFields;
vlbAdjustIntegralHeight;
vlbCalcFontFields;
vlbInitScrollInfo;
end;
end;
end;
procedure TOvcCustomVirtualListBox.SetItemIndex(Index : LongInt);
{-change the currently selected item}
begin
{verify valid index}
if Index > lHighIndex then
if lHighIndex < 0 then
Index := -1
else
Index := lHighIndex;
{do we need to do any more}
if (Index = FItemIndex) then
Exit;
{erase current selection}
InvalidateItem(FItemIndex);
{if Index <> -1 then}
DoOnSelect(FItemIndex, False);
{set the newly selected item index}
FItemIndex := Index;
{lFocusedIndex := -1;}
Update;
if csDesigning in ComponentState then
Exit;
{vlbMakeItemVisible(Index);}
if FItemIndex > -1 then begin
vlbMakeItemVisible(Index);
DoOnSelect(FItemIndex, True);
end;
if FItemIndex <> -1 then
vlbSetFocusedIndex(FItemIndex)
else
vlbSetFocusedIndex(0);
DrawItem(FItemIndex);
{notify of an index change}
if not MouseCapture then
SimulatedClick;
end;
procedure TOvcCustomVirtualListBox.SimulatedClick;
begin
Click;
end;
function TOvcCustomVirtualListBox.IsValidIndex(Index : LongInt) : Boolean;
begin
Result := (Index >= 0) and (Index <= lHighIndex);
end;
procedure TOvcCustomVirtualListBox.SetMultiSelect(Value : Boolean);
{-set ability to select multiple items}
begin
if (csDesigning in ComponentState) or (csLoading in ComponentState) then
if Value <> FMultiSelect then
FMultiSelect := Value;
end;
procedure TOvcCustomVirtualListBox.InternalSetNumItems(Value : LongInt; Paint, UpdateIndices : Boolean);
{-set the number of items in the list box}
var
OldNumItems : LongInt;
begin
if Value <> FNumItems then begin
if (Value < 0) then
{$IFDEF MSWINDOWS}
Value := MaxLongInt;
{$ELSE}
Value := vlDefNumItems;
{$ENDIF}
OldNumItems := FNumItems;
{set new item index}
FNumItems := Value;
{reset high index}
lHighIndex := Pred(FNumItems);
{reset horizontal offset}
lHDelta := 0;
{reset selected item}
if UpdateIndices then
if not (csLoading in ComponentState) then begin
if ItemIndex >= FNumItems then
ItemIndex := -1;
if TopIndex + lRows > FNumItems then
ForceTopIndex(FNumItems - 1, True)
else
ForceTopIndex(TopIndex, False);
end;
if Paint and ((NumItems <= lRows) or (OldNumItems <= lRows)) then
Repaint;
vlbInitScrollInfo;
end;
end;
procedure TOvcCustomVirtualListBox.SetNumItems(Value : LongInt);
{-set the number of items in the list box}
begin
InternalSetNumItems(Value, True, True);
end;
procedure TOvcCustomVirtualListBox.SetRowHeight(Value : Integer);
{-set height of cell row}
begin
if Value <> FRowHeight then begin
FRowHeight := Value;
if not (csLoading in ComponentState) then
AutoRowHeight := False;
vlbCalcFontFields;
vlbAdjustIntegralHeight;
vlbCalcFontFields;
vlbInitScrollInfo;
Refresh;
end;
end;
procedure TOvcCustomVirtualListBox.SetScrollBars(const Value : TScrollStyle);
{-set use of vertical and horizontal scroll bars}
begin
if Value <> FScrollBars then begin
FScrollBars := Value;
lHaveVS := (FScrollBars = ssVertical) or (FScrollBars = ssBoth);
lHaveHS := (FScrollBars = ssHorizontal) or (FScrollBars = ssBoth);
{$IFNDEF LCL}
RecreateWnd;
{$ELSE}
MyMisc.RecreateWnd(Self);
{$ENDIF}
end;
end;
procedure TOvcCustomVirtualListBox.SetShowHeader(Value : Boolean);
{-set show flag for the header}
begin
if Value <> FShowHeader then begin
FShowHeader := Value;
Refresh;
end;
end;
procedure TOvcCustomVirtualListBox.SetTabStops(const Tabs : array of Integer);
{-set tab stop positions}
var
I : Integer;
begin
HandleNeeded;
lNumTabStops := High(Tabs)+1;
if lNumTabStops > vlbMaxTabStops then
lNumTabStops := vlbMaxTabStops;
for I := 0 to Pred(lNumTabStops) do
lTabs[I] := Tabs[I] * lDlgUnits;
end;
procedure TOvcCustomVirtualListBox.ForceTopIndex(Index : LongInt; ThumbTracking : Boolean);
{-set the index of the first visible entry in the list}
var
DY : LongInt;
SaveD : LongInt;
ClipBox,
TmpArea,
ClipArea : TRect;
Inv : TRect;
begin
if (Index >= 0) and (Index <= lHighIndex) then begin
Update;
SaveD := FTopIndex;
{if we can't make the requested item the top one, at least show it}
if Index + lRows -1 <= lHighIndex then
FTopIndex := Index
else
FTopIndex := lHighIndex - lRows + 1;
{check for valid index}
if FTopIndex < 0 then
FTopIndex := 0;
if FTopIndex = SaveD then
Exit;
vlbSetVScrollPos;
ClipArea := ClientRect;
{adjust top of the clipping region to exclude the header, if any}
if FShowHeader then with ClipArea do
Top := Top + FRowHeight;
{$IFNDEF LCL}
if GetClipBox(Canvas.Handle, ClipBox) <> SIMPLEREGION then
{$ELSE} //Something about code below doesn't work so just always InvalidateRect
// for now as workaround. If bug is in ScrollCanvas, then InsertItemsAt
// and DeleteItemsAt will probably also need a similar workaround.
if GetClipBox(Canvas.Handle, @ClipBox) <> Region_Error then
{$ENDIF}
InvalidateRect(Handle, @ClipArea, True)
else begin
InterSectRect(ClipArea, ClipArea, ClipBox);
TmpArea := ClipArea;
TmpArea.Bottom := ClipArea.Bottom;
{adjust bottom of the clipping region to an even number of rows}
with ClipArea do
Bottom := (Bottom div FRowHeight) * FRowHeight;
TmpArea.Top := ClipArea.Bottom;
{if ThumbTracking then
InvalidateRect(Handle, @ClipArea, True)
else} begin
DY := (SaveD - FTopIndex);
if Abs(DY) > lRows then
DY := lRows;
DY := DY * FRowHeight;
Update;
{Make sure the canvas is updated,
because we will be validating the scrolled portion.}
Inv := ScrollCanvas(Canvas, ClipArea, False, DY, (not ThumbTracking) and FSmoothScroll);
InvalidateRect(Handle, @Inv, False);
InvalidateRect(Handle, @TmpArea, False);
if SaveD <> FTopIndex then begin
DoOnTopIndexChanged(FTopIndex);
SaveD := FTopIndex;
end;
Update;
end;
end;
vlbSetFocusedIndex(FItemIndex);
{notify that top index has changed}
if SaveD <> FTopIndex then
DoOnTopIndexChanged(FTopIndex);
end;
end;
procedure TOvcCustomVirtualListBox.SetTopIndex(Index : LongInt);
{-set the index of the first visible entry in the list}
begin
if csDesigning in ComponentState then
Exit;
if Index <> FTopIndex then
ForceTopIndex(Index, False);
end;
procedure TOvcCustomVirtualListBox.vlbAdjustIntegralHeight;
begin
if (csDesigning in ComponentState) and
not (csLoading in ComponentState) then
if FIntegralHeight then
if ClientHeight mod FRowHeight <> 0 then
ClientHeight := (ClientHeight div FRowHeight) * FRowHeight;
end;
procedure TOvcCustomVirtualListBox.vlbCalcFontFields;
var
Alpha : string;
begin
if not HandleAllocated then
Exit;
Alpha := GetOrphStr(SCAlphaString);
{set the canvas font}
Canvas.Font := Self.Font;
{determine the height of one row}
if FAutoRowHeight and not (csLoading in ComponentState) then
FRowHeight := Canvas.TextHeight(GetOrphStr(SCTallLowChars)) + lVMargin;
lRows := (ClientHeight div FRowHeight)-Ord(FShowHeader);
if lRows < 1 then
lRows := 1;
{calculate the base dialog unit for tab spacing}
lDlgUnits := (Canvas.TextWidth(Alpha) div Length(Alpha)) div 4
end;
procedure TOvcCustomVirtualListBox.vlbClearAllItems;
{-clear the highlight from all items}
begin
vlbSetAllItemsPrim(False);
end;
procedure TOvcCustomVirtualListBox.vlbClearSelRange(First, Last : LongInt);
{-clear the selection for the given range of indexes}
begin
vlbSelectRangePrim(First, Last, False);
end;
procedure TOvcCustomVirtualListBox.vlbColorChanged(AColor: TObject);
{-a color has changed, refresh display}
begin
Refresh;
end;
procedure TOvcCustomVirtualListBox.vlbDrawFocusRect(Index : LongInt);
{-draw the focus rectangle}
var
CR : TRect;
begin
if Index < 0 then exit;
if Focused then begin
if (Index >= FTopIndex) and (Index-FTopIndex <= Pred(lRows)) then begin
CR := ClientRect;
CR.Top := (Index-FTopIndex+Ord(FShowHeader))*FRowHeight;
CR.Bottom := CR.Top + FRowHeight;
Canvas.DrawFocusRect(CR);
end;
end;
lFocusedIndex := Index;
end;
procedure TOvcCustomVirtualListBox.vlbDragSelection(First, Last : LongInt);
{-drag the selection}
var
I : LongInt;
OutSide : Boolean;
begin
{set new active item}
vlbNewActiveItem(Last);
{remove selection from visible selected items not in range}
for I := FTopIndex to FTopIndex+Pred(lRows) do begin
if First <= Last then
OutSide := (I < First) or (I > Last)
else
OutSide := (I < Last) or (I > First);
if DoOnIsSelected(I) and OutSide then
InvalidateItem(I);
end;
{deselect all items}
DoOnSelect(-1, False);
{select new range}
vlbSetSelRange(First, Last);
vlbSetFocusedIndex(Last);
Update;
end;
procedure TOvcCustomVirtualListBox.vlbDrawHeader;
{-draw the header and text}
var
R : TRect;
Buf : array[0..255] of AnsiChar;
S : PAnsiChar;
DX : Integer;
begin
{get the printable area of the header text}
StrPCopy(Buf, FHeader);
if lHDelta >= LongInt(StrLen(Buf)) then
S := ' ' {space to erase last character from header}
else
S := @Buf[lHDelta];
Canvas.Font := Font;
with Canvas do begin
{draw header text}
Brush.Color := FHeaderColor.BackColor;
Font.Color := FHeaderColor.TextColor;
R := Bounds(0, 0, Width, FRowHeight-1);
{clear the line}
Canvas.FillRect(R);
if S <> nil then
if FUseTabStops then begin
DX := 0;
if lHDelta > 0 then begin
{measure portion of string to the left of the window}
DX := LOWORD(GetTabbedTextExtent(Canvas.Handle, Buf, lHDelta,
lNumTabStops, lTabs));
end;
TabbedTextOut(Canvas.Handle, 2, 0,
S, StrLen(S), lNumTabStops, lTabs, -DX)
end else
ExtTextOut(Canvas.Handle, 2, 0, ETO_OPAQUE + ETO_CLIPPED,
@R, S, StrLen(S), nil);
{draw border line}
Pen.Color := clBlack;
PolyLine([Point(R.Left, R.Bottom), Point(R.Right, R.Bottom)]);
{draw ctl3d highlight}
if Ctl3D then begin
Pen.Color := clBtnHighlight;
PolyLine([Point(R.Left, R.Bottom-1),
Point(R.Left, R.Top),
Point(R.Right, R.Top)]);
end;
end;
end;
procedure TOvcCustomVirtualListBox.vlbExtendSelection(Index : LongInt);
{-process Shift-LMouseBtn}
begin
{verify valid index}
if Index < 0 then
Index := 0
else if Index > lHighIndex then
Index := lHighIndex;
{clear current selections}
vlbClearAllItems;
{set selection for all items from the active one to the currently selected item}
vlbSetSelRange(lAnchor, Index);
{set new active item}
FItemIndex := Index;
vlbSetFocusedIndex(FItemIndex);
Update;
end;
procedure TOvcCustomVirtualListBox.vlbHScrollPrim(Delta : Integer);
var
SaveD : LongInt;
begin
SaveD := lHDelta;
if Delta < 0 then
if Delta > lHDelta then
lHDelta := 0
else
Inc(lHDelta, Delta)
else
if LongInt(lHDelta)+Delta > LongInt(FColumns) then
lHDelta := FColumns
else
Inc(lHDelta, Delta);
if lhDelta < 0 then
lhDelta := 0;
if lHDelta <> SaveD then begin
vlbSetHScrollPos;
Refresh;
end;
end;
procedure TOvcCustomVirtualListBox.vlbInitScrollInfo;
{-setup scroll bar range and initial position}
begin
if not HandleAllocated then
Exit;
{initialize scroll bars, if any}
vlbSetVScrollRange;
vlbSetVScrollPos;
vlbSetHScrollRange;
vlbSetHScrollPos;
end;
procedure TOvcCustomVirtualListBox.vlbMakeItemVisible(Index : LongInt);
{-make sure the item is visible}
begin
if Index < FTopIndex then
TopIndex := Index
else if Index+LongInt($80000000) > (FTopIndex+Pred(lRows))+LongInt($80000000) then begin
TopIndex := Index-Pred(lRows);
if FTopIndex < 0 then
TopIndex := 0;
end;
end;
procedure TOvcCustomVirtualListBox.vlbNewActiveItem(Index : LongInt);
{-set the currently selected item}
begin
{verify valid index}
if Index < 0 then
Index := 0
else if Index > lHighIndex then
Index := lHighIndex;
{set the newly selected item index}
FItemIndex := Index;
vlbMakeItemVisible(Index);
DoOnSelect(Index, True);
InvalidateItem(Index);
end;
function TOvcCustomVirtualListBox.vlbScaleDown(N : LongInt) : Integer;
begin
Result := N div lDivisor;
end;
function TOvcCustomVirtualListBox.vlbScaleUp(N : LongInt) : LongInt;
begin
Result := N * lDivisor;
end;
procedure TOvcCustomVirtualListBox.vlbSelectRangePrim(First, Last : LongInt; Select : Boolean);
{-change the selection for the given range of indexes}
var
I : LongInt;
begin
if First <= Last then begin
for I := First to Last do begin
DoOnSelect(I, Select);
InvalidateItem(I);
end;
end else begin
for I := First downto Last do begin
DoOnSelect(I, Select);
InvalidateItem(I);
end;
end;
end;
procedure TOvcCustomVirtualListBox.vlbSetAllItemsPrim(Select : Boolean);
{-primitive routine thats acts on all items}
var
I : LongInt;
LastIndex : LongInt;
begin
{determine highest index to test}
LastIndex := FTopIndex+Pred(lRows);
if LastIndex > Pred(FNumItems) then
LastIndex := Pred(FNumItems);
{invalidate items that require repainting}
for I := FTopIndex to LastIndex do
if DoOnIsSelected(I) <> Select then
InvalidateItem(I);
{select or deselect all items}
DoOnSelect(-1, Select);
end;
procedure TOvcCustomVirtualListBox.vlbSetFocusedIndex(Index : LongInt);
{-set focus index to this item. invalidate previous}
begin
if Index <> lFocusedIndex then begin
InvalidateItem(lFocusedIndex);
lFocusedIndex := Index;
InvalidateItem(lFocusedIndex);
end;
end;
{ rewritten - see below
procedure TOvcCustomVirtualListBox.vlbSetHScrollPos;
begin
if lHaveHS then
SetScrollPos(Handle, SB_HORZ, lHDelta, True);
end;
}
{ rewritten}
procedure TOvcCustomVirtualListBox.vlbSetHScrollPos;
var
SI : TScrollInfo;
begin
if lHaveHS and HandleAllocated then begin
with SI do begin
cbSize := SizeOf(SI);
fMask := SIF_RANGE or SIF_PAGE or SIF_POS;
nMin := 0;
nMax := FColumns;
nPage := FColumns div 2;
nPos := lhDelta;
nTrackPos := nPos;
end;
SetScrollInfo(Handle, SB_HORZ, SI, True);
end;
end;
{ rewritten - see below
procedure TOvcCustomVirtualListBox.vlbSetHScrollRange;
begin
if lHaveHS then
SetScrollRange(Handle, SB_HORZ, 0, FColumns, False);
end;
}
{ rewritten}
procedure TOvcCustomVirtualListBox.vlbSetHScrollRange;
{var
SI : TScrollInfo;}
begin
vlbSetHScrollPos;
(*
if lHaveHS then
begin
with SI do
begin
fMask := {SIF_PAGE + }SIF_RANGE;
nMin := 1;
nMax := FColumns - ClientWidth;
//nPage := nMax div 10;
cbSize := SizeOf(SI);
end;
SetScrollInfo(Handle, SB_HORZ, SI, False);
end;
*)
end;
procedure TOvcCustomVirtualListBox.vlbSetSelRange(First, Last : LongInt);
{-set the selection on for the given range of indexes}
begin
vlbSelectRangePrim(First, Last, True);
end;
procedure TOvcCustomVirtualListBox.vlbSetVScrollPos;
var
SI : TScrollInfo;
begin
if not HandleAllocated then
Exit;
with SI do begin
cbSize := SizeOf(SI);
fMask := SIF_RANGE or SIF_PAGE or SIF_POS;
nMin := 0;
nMax := Pred(lVSHigh);
nPage := lRows;
nPos := vlbScaleDown(FTopIndex);
nTrackPos := nPos;
end;
SetScrollInfo(Handle, SB_VERT, SI, True);
end;
procedure TOvcCustomVirtualListBox.vlbSetVScrollRange;
var
ItemRange : LongInt;
begin
ItemRange := FNumItems;
lDivisor := 1;
if ItemRange < lRows then
lVSHigh := 1
{$IFDEF MSWINDOWS}
else if ItemRange <= High(SmallInt) then
lVSHigh := ItemRange
else begin
lDivisor := 2*(ItemRange div 32768);
lVSHigh := ItemRange div lDivisor;
end;
{$ELSE} //lDivisor not needed apparently (and causes clicks to scroll >1 item).
else
lVSHigh := ItemRange;
{$ENDIF}
if lHaveVS then
if not ((FNumItems > lRows) or (csDesigning in ComponentState)) then
lvSHigh := 0
else
else
lvSHigh := 0;
vlbSetVScrollPos;
end;
procedure TOvcCustomVirtualListBox.vlbToggleSelection(Index : LongInt);
{-process Ctrl-LMouseBtn}
var
WasSelected : Boolean;
begin
if (Index < 0) or (Index > lHighIndex) then
exit;
{toggle highlight}
WasSelected := DoOnIsSelected(Index);
DoOnSelect(Index, not WasSelected);
vlbSetFocusedIndex(Index);
DrawItem(Index);
{set new active item}
FItemIndex := Index;
{and anchor point}
lAnchor := Index;
end;
procedure TOvcCustomVirtualListBox.vlbValidateItem(Index : LongInt);
{-validate the area for this item}
var
CR : TRect;
begin
if (Index >= FTopIndex) and (Index-FTopIndex < lRows) then begin {visible?}
CR := Rect(0, (Index-FTopIndex+Ord(FShowHeader))*FRowHeight, ClientWidth, 0);
CR.Bottom := CR.Top+FRowHeight;
ValidateRect(Handle, @CR);
end;
end;
procedure TOvcCustomVirtualListBox.vlbVScrollPrim(Delta : Integer);
var
I : LongInt;
begin
I := FTopIndex+Delta;
if I < 0 then
if Delta > 0 then
I := lHighIndex
else
I := 0
else if (I > lHighIndex-Pred(lRows)) then begin
if lHighIndex > Pred(lRows) then
I := lHighIndex-Pred(lRows)
else
I := 0;
end;
SetTopIndex(I);
end;
procedure TOvcCustomVirtualListBox.WMChar(var Msg : TWMChar);
var
L : LongInt;
begin
inherited;
L := DoOnCharToItem(AnsiChar(Msg.CharCode));
if (L >= 0) and (L <= lHighIndex) then
SetItemIndex(L);
end;
procedure TOvcCustomVirtualListBox.WMEraseBkgnd(var Msg : TWMEraseBkGnd);
begin
{indicate that we have processed this message}
Msg.Result := 1;
end;
procedure TOvcCustomVirtualListBox.WMGetDlgCode(var Msg : TWMGetDlgCode);
begin
inherited;
Msg.Result := Msg.Result or DLGC_WANTCHARS or DLGC_WANTARROWS;
end;
procedure TOvcCustomVirtualListBox.WMHScroll(var Msg : TWMHScroll);
begin
case Msg.ScrollCode of
SB_LINERIGHT : vlbHScrollPrim(+1);
SB_LINELEFT : vlbHScrollPrim(-1);
SB_PAGERIGHT : vlbHScrollPrim(+10);
SB_PAGELEFT : vlbHScrollPrim(-10);
SB_THUMBPOSITION, SB_THUMBTRACK :
if lHDelta <> Msg.Pos then begin
lHDelta := Msg.Pos;
vlbSetHScrollPos;
Refresh;
end;
end;
end;
procedure TOvcCustomVirtualListBox.WMKeyDown(var Msg : TWMKeyDown);
var
I : LongInt;
Cmd : Word;
begin
inherited;
Cmd := Controller.EntryCommands.Translate(TMessage(Msg));
if Cmd <> ccNone then begin
{filter invalid commands}
case Cmd of
ccExtendHome, ccExtendEnd, ccExtendPgUp,
ccExtendPgDn, ccExtendUp, ccExtendDown :
if not FMultiSelect then
Exit;
end;
case Cmd of
ccLeft :
if lHaveHs then begin
if lHDelta > 0 then begin
Dec(lHDelta);
vlbSetHScrollPos;
Refresh;
end;
end else begin
if FItemIndex > 0 then begin
vlbClearAllItems;
SetItemIndex(FItemIndex-1);
lAnchor := FItemIndex;
end;
end;
ccRight :
if lHaveHs then begin
if lHDelta < FColumns then begin
Inc(lHDelta);
vlbSetHScrollPos;
Refresh;
end;
end else begin
if FItemIndex < lHighIndex then begin
vlbClearAllItems;
SetItemIndex(FItemIndex+1);
lAnchor := FItemIndex;
end;
end;
ccUp :
if FItemIndex > 0 then begin
vlbClearAllItems;
SetItemIndex(FItemIndex-1);
lAnchor := FItemIndex;
end;
ccDown :
if FItemIndex < lHighIndex then begin
vlbClearAllItems;
SetItemIndex(FItemIndex+1);
lAnchor := FItemIndex;
end;
ccHome :
if FItemIndex <> 0 then begin
vlbClearAllItems;
SetItemIndex(0);
lAnchor := FItemIndex;
end;
ccEnd :
if (FNumItems > 0) and (FItemIndex <> lHighIndex) then begin
vlbClearAllItems;
SetItemIndex(lHighIndex);
lAnchor := FItemIndex;
end;
ccPrevPage :
if FNumItems > 0 then begin
if lRows = 1 then
I := Pred(FItemIndex)
else
I := FItemIndex-Pred(lRows);
if I < 0 then
I := 0;
if I <> FItemIndex then begin
vlbClearAllItems;
SetItemIndex(I);
lAnchor := FItemIndex;
end;
end;
ccNextPage :
if FNumItems > 0 then begin
if lRows = 1 then begin
if FItemIndex < lHighIndex then
I := Succ(FItemIndex)
else
I := lHighIndex;
end else if FItemIndex <= lHighIndex-Pred(lRows) then
I := FItemIndex+Pred(lRows)
else
I := lHighIndex;
if I <> FItemIndex then begin
vlbClearAllItems;
SetItemIndex(I);
lAnchor := FItemIndex;
end;
end;
ccExtendHome :
if FItemIndex > 0 then begin
vlbNewActiveItem(0);
vlbExtendSelection(0);
end;
ccExtendEnd :
if FItemIndex < lHighIndex then begin
vlbNewActiveItem(lHighIndex);
vlbExtendSelection(lHighIndex);
end;
ccExtendPgUp :
begin
I := FItemIndex-Pred(lRows);
vlbNewActiveItem(I);
vlbExtendSelection(I);
end;
ccExtendPgDn :
begin
I := FItemIndex+Pred(lRows);
vlbNewActiveItem(I);
vlbExtendSelection(I);
end;
ccExtendUp :
begin
I := FItemIndex-1;
vlbNewActiveItem(I);
vlbExtendSelection(I);
end;
ccExtendDown :
begin
I := FItemIndex+1;
vlbNewActiveItem(I);
vlbExtendSelection(I);
end;
else
{do user command notification for user commands}
if Cmd >= ccUserFirst then
DoOnUserCommand(Cmd);
end;
{indicate that this message was processed}
Msg.Result := 0;
end;
end;
procedure TOvcCustomVirtualListBox.WMKillFocus(var Msg : TWMKillFocus);
begin
inherited;
{re-draw focused item to erase focus rect}
DrawItem(lFocusedIndex);
end;
procedure TOvcCustomVirtualListBox.WMLButtonDown(var Msg : TWMLButtonDown);
var
I : LongInt;
LastI : LongInt;
LButton : Byte;
CtrlKeyDown : Boolean;
ShiftKeyDown : Boolean;
function PointToIndex : LongInt;
var
Pt : TPoint;
begin
GetCursorPos(Pt);
Pt := ScreenToClient(Pt);
if Pt.Y < Ord(FShowHeader)*FRowHeight then begin
{speed up as the cursor moves farther away}
Result := FTopIndex+(Pt.Y div FRowHeight)-1;
if Result < 0 then
Result := 0;
end else if Pt.Y >= ClientHeight then begin
{speed up as the cursor moves farther away}
Result := FTopIndex+(Pt.Y div FRowHeight);
if Result > lHighIndex then
Result := lHighIndex;
end else begin
{convert to an index}
Result := FTopIndex-Ord(FShowHeader)+(Pt.Y div FRowHeight);
if ClientHeight mod FRowHeight > 0 then
if Result > FTopIndex-1 + lRows then
Result := FTopIndex-1 + lRows;
end;
end;
var
ItemNo : Integer;
ShiftState: TShiftState;
begin
ShiftState := KeysToShiftState(Msg.Keys);
if (DragMode = dmAutomatic) and FMultiSelect then
begin
if not (ssShift in ShiftState) or (ssCtrl in ShiftState) then
begin
ItemNo := ItemAtPos(SmallPointToPoint(Msg.Pos), True);
if (ItemNo >= 0) and (DoOnIsSelected(ItemNo)) then
begin
BeginDrag (False);
Exit;
end;
end;
end;
inherited;
if (DragMode = dmAutomatic) and not (FMultiSelect and
((ssCtrl in ShiftState) or (ssShift in ShiftState))) then
BeginDrag(False);
if MousePassThru then exit;
{solve problem with minimized modeless dialogs and MDI child windows}
{that contain virtual ListBox components}
if not Focused and CanFocus then
{$IFNDEF LCL}
Windows.SetFocus(Handle);
{$ELSE}
LclIntf.SetFocus(Handle);
{$ENDIF}
{is this click on the header?}
if FShowHeader and (Msg.YPos < FRowHeight) then begin
DoOnClickHeader(Point(Msg.XPos, Msg.YPos));
Exit;
end;
if (FNumItems <> 0) then begin
{get the actual left button}
LButton := GetLeftButton;
{get the key state}
if FMultiSelect then begin
{$IFNDEF LCLCarbon}
CtrlKeyDown := GetKeyState(VK_CONTROL) and $8000 <> 0;
{$ELSE} {Cmd+click used on Mac for non-contiguous selection; see it as VK_LWIN}
CtrlKeyDown := GetKeyState(VK_LWIN) and $8000 <> 0;
{$ENDIF}
ShiftKeyDown := GetKeyState(VK_SHIFT) and $8000 <> 0;
end else begin
CtrlKeyDown := False;
ShiftKeyDown := False;
end;
if CtrlKeyDown then
vlbToggleSelection(PointToIndex)
else if ShiftKeyDown then
vlbExtendSelection(PointToIndex)
else begin
vlbClearAllItems;
{reselect the active item}
if FItemIndex <> -1 then begin
DoOnSelect(FItemIndex, True);
vlbSetFocusedIndex(FItemIndex);
end;
{watch the mouse position while the left button is down}
LastI := -1;
repeat
I := PointToIndex;
if I <= lHighIndex then
if not FMultiSelect or (LastI = -1) then begin
SetItemIndex(I);
lAnchor := I;
LastI := I;
end else begin
{extend/shrink the selection to follow the mouse}
if I <> LastI then begin
vlbDragSelection(lAnchor, I);
LastI := I;
end;
end;
Application.ProcessMessages; {Gasp}
{$IFDEF MSWINDOWS}
until ({$IFNDEF LCL} GetAsyncKeyState(LButton) {$ELSE} GetKeyState(LButton) {$ENDIF} and $8000 = 0)
{$ELSE} //GTK GetKeyState returns 0
until (not (csLButtonDown in ControlState))
{$ENDIF}
or Dragging or (GetCapture <> Handle);
end;
end;
end;
procedure TOvcCustomVirtualListBox.WMLButtonDblClk(var Msg : TWMLButtonDblClk);
begin
{is this click below the header, if any}
if (Msg.YPos > FRowHeight * Ord(FShowHeader)) then
inherited
else
{say we processed this message}
Msg.Result := 0;
end;
procedure TOvcCustomVirtualListBox.WMMouseActivate(var Msg : TWMMouseActivate);
begin
if (csDesigning in ComponentState) or (GetFocus = Handle) then
inherited
else begin
if Controller.ErrorPending then
Msg.Result := MA_NOACTIVATEANDEAT
else
Msg.Result := MA_ACTIVATE;
end;
end;
procedure TOvcCustomVirtualListBox.WMSetFocus(var Msg : TWMSetFocus);
begin
inherited;
Update;
DrawItem(lFocusedIndex);
end;
procedure TOvcCustomVirtualListBox.WMSize(var Msg : TWMSize);
begin
if FRowHeight > 0 then begin
{integral font height adjustment}
vlbCalcFontFields;
vlbAdjustIntegralHeight;
{$IFDEF LCL} //Make sure calling code knows about any change in height.
if (csDesigning in ComponentState) and
not (csLoading in ComponentState) then
if FIntegralHeight then
Msg.Height := Height; //was ClientHeight, but no longer works on Windows
{$ENDIF}
vlbCalcFontFields;
vlbInitScrollInfo;
{reposition so that items are displayed at bottom of list}
if lRows + FTopIndex - 1 >= FNumItems then
if NumItems-lRows >= 0 then
TopIndex := NumItems-lRows
else
TopIndex := 0;
end;
inherited;
end;
procedure TOvcCustomVirtualListBox.WMVScroll(var Msg : TWMVScroll);
var
I : LongInt;
begin
case Msg.ScrollCode of
SB_LINEUP : vlbVScrollPrim(-1);
SB_LINEDOWN : vlbVScrollPrim(+1);
SB_PAGEDOWN : vlbVScrollPrim(+Pred(lRows));
SB_PAGEUP : vlbVScrollPrim(-Pred(lRows));
SB_THUMBPOSITION, SB_THUMBTRACK :
begin
if Msg.Pos = 0 then
I := 0
else if Msg.Pos = lVSHigh then
if lRows >= FNumItems then
I := 0
else
I := lHighIndex-Pred(lRows)
else
I := vlbScaleUp(Msg.Pos);
ForceTopIndex(I,True);
end;
end;
end;
{ new}
procedure TOvcCustomVirtualListBox.SetColumns(const Value: Integer);
begin
if Value <> FColumns then begin
FColumns := Value;
vlbInitScrollInfo;
end;
end;
end.