unit SRVCBDropList;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ImgList, SRVTypeItems, SRVControl, SRVSkinManager, SRVSBar;

type
  TGetItemHeight = function(Index : Integer) : Integer of object;
  TCalculateItemCountToHeight = function(Height : Integer) : Integer of object;
  TCalculateDropItemCount = function(FirstItem, OffY, Height : Integer) : Integer of object;
  TGetItem = function(Position : Integer) : Integer of object;
  TUpdate = procedure of object;
  TCalculateItemCount = function : Integer of object;
  TCBSetItemIndex = procedure (Value : Integer) of object;
  TCBItemHasParent = function(Index : Integer) : Boolean of object;
  TCBGetParent = function(Index : Integer) : Integer of object;
  TCBItemHint = procedure (Sender : TControl; Index : Integer) of object;

  TFSRVCBList = class(TForm)
    procedure FormDeactivate(Sender: TObject);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormMouseWheel(Sender: TObject; Shift: TShiftState;
      WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
  private
    { Private declarations }
    FScrollBar: TSRVScrollBar;
    MouseIn : Boolean;
    isMouseDown : Boolean;
    IsByse : Boolean;
    procedure UpdateBuffer;
    procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
    procedure WMEraseBkgnd(var Message: TWMPaint); message WM_ERASEBKGND;
    procedure HookOnScroll(Sender: TObject; ScrollCode: TScrollCode;
      var ScrollPos: Integer);
    procedure WMRepaintControl(var Message: TMessage); message WM_REPAINT_CONTROL;
  public
    { Public declarations }
    UseItemFontNames : Boolean;
    AutoDropDown : Boolean;
    AControl : TWinControl;
    Buffer : TBitmap;
    FirstItem : Integer;
    OffX : Integer;
    OffY : Integer;
    Option : TSRVCBOptions;
    lockUpdate : Integer;
    SelectedItemBorderColor : TColor;
    SelectedItemBackColor : TColor;
    SelectedItemTextColor : TColor;
    HotItemBorderColor : TColor;
    HotItemBackColor : TColor;
    HotItemTextColor : TColor;
    ItemIndex : Integer;
    MouseMoveIndex : Integer;
    CurItemIndex : Integer;
    ItemHeight : Integer;
    DropDownCount : Integer;
    Items : TSRVCBoxItemCollection;
    ScaleX, ScaleY : Single;
    ImgList: TCustomImageList;
    GetItemHeight : TGetItemHeight;
    CalculateDropItemCount : TCalculateDropItemCount;
    CalculateShowItemCount : TCalculateDropItemCount;
    GetItem : TGetItem;
    CalculateItemCount : TCalculateItemCount;
    CalculateItemCountToHeight : TCalculateItemCountToHeight;
    BeginUpdate : TUpdate;
    EndUpdate : TUpdate;
    ItemHasParent : TCBItemHasParent;
    ItemHint : TCBItemHint;
    GetParent : TCBGetParent;
    FOnDrawItem: TDrawItemEvent;
    SetItemIndex : TCBSetItemIndex;
    SkinManager : TSRVSkinManager;
    SkinSchemeIndex : Integer;
    ItemSkinSchemeIndex : Integer;
    procedure Paint; override;
    procedure Invalidate; override;
    procedure Resize; override;
    function GetNextItem(Index, Delta : Integer; isMouse : Boolean) : Integer;
    function GetPriorItem(Index, Delta : Integer; isMouse : Boolean) : Integer;
    function GetTopItem(ItemIndex : Integer) : Integer;
    procedure Reset;
    property ScrollBar: TSRVScrollBar read FScrollBar;
 end;

var
  FSRVCBList: TFSRVCBList;

implementation

uses Math;

{$R *.DFM}

procedure TFSRVCBList.FormDeactivate(Sender: TObject);
begin
  if not AutoDropDown then
    Close;
end;

function TFSRVCBList.GetPriorItem(Index, Delta : Integer; isMouse : Boolean) : Integer;
begin
  Result := Index;
  Index := Index + Delta;
  while (Index >= 0) and
        (((not TSRVCBoxItem(Items.Items[Index]).Enabled) and not isMouse) or
        (not TSRVCBoxItem(Items.Items[Index]).OpenParent) or
         not (srvioCanSelect in TSRVCBoxItem(Items.Items[Index]).Option)) do
    Dec(Index);
  if (Index >= 0) then
    Result := Index;
end;

function TFSRVCBList.GetNextItem(Index, Delta : Integer; isMouse : Boolean) : Integer;
begin
  Result := Index;
  Index := Index + Delta;
  while (Index < Items.Count) and
        (((not TSRVCBoxItem(Items.Items[Index]).Enabled) and not isMouse) or
        (not TSRVCBoxItem(Items.Items[Index]).OpenParent) or
         not (srvioCanSelect in TSRVCBoxItem(Items.Items[Index]).Option)) do
    Inc(Index);
  if (Index < Items.Count) then
    Result := Index;
end;


procedure TFSRVCBList.Resize;
begin
  inherited;

  Buffer.Width := Width;
  Buffer.Height := Height;
  UpdateBuffer;
end;

procedure TFSRVCBList.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var
     yy, ItemH, yItem, index, OldMouseMoveIndex : Integer;
     Message: TWMMouse;
begin
  yy := Round(Y / ScaleY);

  if FScrollBar.Visible and
    (X >= Width-1 - Round(FScrollBar.Width * ScaleX)) then begin
    // in scrollbar
    Message.XPos := Round((X - Width-1 + FScrollBar.Width * ScaleX) / ScaleX);
    Message.YPos := Round(Y / ScaleY);
    with TMessage(Message) do
      PostMessage(FScrollBar.Handle, WM_MOUSEMOVE, WParam, LParam);
    Paint;
    Exit;
  end else begin
    If not IsByse and FScrollBar.InMouse then begin
      IsByse := True;
      with Message do
        SendMessage(FScrollBar.Handle, CM_MOUSELEAVE, 0, 0);
      IsByse := False;
    end;
    // Items
    OldMouseMoveIndex := MouseMoveIndex;
    yItem:= 0;
    for index := FirstItem to CalculateShowItemCount(FirstItem, 0, Round(Height / ScaleY))-1 do begin
      if not TSRVCBoxItem(Items.Items[index]).OpenParent then
        continue;
      ItemH := GetItemHeight(index);
      if (yy >= yItem) and (yy < yItem + ItemH) then begin
        MouseMoveIndex := index;
        break;
      end;
      yItem := yItem + ItemH;
    end;

    if (MouseMoveIndex < -1) or (MouseMoveIndex >= Items.Count) then
      MouseMoveIndex := OldMouseMoveIndex
    else if (MouseMoveIndex >= 0) and
       not TSRVCBoxItem(Items.Items[MouseMoveIndex]).Enabled then
      MouseMoveIndex := OldMouseMoveIndex;
  end;
  CurItemIndex := MouseMoveIndex;
  Paint;
  ItemHint(Self, MouseMoveIndex);
end;

procedure TFSRVCBList.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
     Message: TWMMouse;
begin
  // ScrollBar
  if FScrollBar.Visible and
    (X >= Width-1 - Round(FScrollBar.Width * ScaleX)) then begin
    // in scrollbar
    Message.XPos := Round((X - Width-1 + FScrollBar.Width * ScaleX) / ScaleX);
    Message.YPos := Round(Y / ScaleY);
    with TMessage(Message) do
      PostMessage(FScrollBar.Handle, WM_LBUTTONDOWN, WParam, LParam);
    Paint;
    Exit;
  end else begin
    // choice
    if (MouseMoveIndex > -1) and (MouseMoveIndex < Items.Count) then begin

      if (srvioCanHaveChildren in TSRVCBoxItem(Items.Items[MouseMoveIndex]).Option) then begin
        if not (srvioCanSelect in TSRVCBoxItem(Items.Items[MouseMoveIndex]).Option) then
          TSRVCBoxItem(Items.Items[MouseMoveIndex]).Expanded := not
            TSRVCBoxItem(Items.Items[MouseMoveIndex]).Expanded;
        if (srvioCanSelect in TSRVCBoxItem(Items.Items[MouseMoveIndex]).Option) then begin
          ItemIndex := MouseMoveIndex;
          Close;
        end;
      end;
    end else begin
      // Items
      ItemIndex := MouseMoveIndex;
      Close;
    end;
  end;
  isMouseDown := True;
  Paint;
end;

procedure TFSRVCBList.FormMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  isMouseDown := False;
end;

procedure TFSRVCBList.FormMouseWheel(Sender: TObject; Shift: TShiftState;
  WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
  Handled := True;
  If ScrollBar.Visible then begin
    BeginUpdate;
    if WheelDelta <= 0 then
      FScrollBar.Position := FScrollBar.Position + Mouse.WheelScrollLines
    else
      FScrollBar.Position := FScrollBar.Position - Mouse.WheelScrollLines;
    FirstItem := GetItem(FScrollBar.Position);
    EndUpdate;
    Paint;
  end;
end;

procedure TFSRVCBList.FormCreate(Sender: TObject);
begin
  MouseIn := False;
  isMouseDown := False;
  IsByse := False;
  lockUpdate := 0;
  Buffer := TBitmap.Create;

  FScrollBar := TSRVScrollBar.Create(Self);
  FScrollBar.Parent := Self;
  FScrollBar.Kind := sbVertical;
  FScrollBar.Width := GetSystemMetrics(SM_CXVSCROLL);
  FScrollBar.ScrollingDelay := 100;
  FScrollBar.SmallChange := 1;
  FScrollBar.LargeChange := 5;
  FScrollBar.TabStop := False;
  FScrollBar.OnScroll := HookOnScroll;
  FScrollBar.Enabled := True;
  FScrollBar.Visible := False;
end;

procedure TFSRVCBList.FormDestroy(Sender: TObject);
begin
  Buffer.Free;
  FScrollBar.Destroy;
  FScrollBar := nil;
end;

procedure TFSRVCBList.UpdateBuffer;
var
  OldMapMode: Integer;
  OldWindowOrg, OldWindowExt, OldViewportOrg, OldViewportExt: TSize;
  i, X, Y, OffImgY, OffTextY, ItemHeight, ImgIndex, PosX, Step : Integer;
  R, RClient : TRect;
  DoSkin : Boolean;

  procedure GetOffset;
  begin
    case TSRVCBoxItem(Items.Items[i]).Alignment of
      taCenter : begin
          PosX := OffX + TSRVCBoxItem(Items.Items[i]).Indent;

          If Assigned(ImgList) and
            (srvioShowImage in TSRVCBoxItem(Items.Items[i]).Option) then
            PosX := PosX + (ImgList.Width + 2);

          if (srvioShowText in TSRVCBoxItem(Items.Items[i]).Option) then begin
            if UseItemFontNames then
              Buffer.Canvas.Font.Name := TSRVCBoxItem(Items.Items[i]).FontName;
            Buffer.Canvas.Font.Style := TSRVCBoxItem(Items.Items[i]).Style;
            PosX := PosX + (Buffer.Canvas.TextWidth(TSRVCBoxItem(Items.Items[i]).Caption) + 2);
          end;

          X := (RClient.Left + RClient.Right - PosX) div 2;
          Step := 1;
        end;
      taRightJustify : begin
        X := RClient.Right;
        Step := -1;
      end;
    else
      X := 1;
      Step := 1;
    end;
  end;

  procedure DrawImage;
  begin
      // draw image
      If Assigned(ImgList) and
         (srvioShowImage in TSRVCBoxItem(Items.Items[i]).Option) then begin
        OffImgY := (ItemHeight - ImgList.Height) div 2;
        If Step > 0 then
          PosX := X + OffX + TSRVCBoxItem(Items.Items[i]).Indent
        else
          PosX := X - OffX - TSRVCBoxItem(Items.Items[i]).Indent -
            ImgList.Width;
        ImgList.Draw(Buffer.Canvas, PosX, Y + OffImgY, ImgIndex);
        X := X + (ImgList.Width + 2) * Step;
      end;
  end;

begin
  if not Assigned(Items) then
    exit;

  Buffer.Canvas.Lock;

  OldMapMode := GetMapMode(Buffer.Canvas.Handle);
  SetMapMode(Buffer.Canvas.Handle, MM_ANISOTROPIC);
  SetWindowOrgEx(Buffer.Canvas.Handle, 0, 0, @OldWindowOrg);
  SetWindowExtEx(Buffer.Canvas.Handle, Width, Height, @OldWindowExt);

  SetViewportOrgEx(Buffer.Canvas.Handle, 0, 0, @OldViewportOrg);
  SetViewportExtEx(Buffer.Canvas.Handle, Round(Width * ScaleX),
     Round(Height * ScaleY), @OldViewportExt);

  RClient := Bounds(0, 0, Round(Width / ScaleX), Round(Height / ScaleY));
  If FScrollBar.Visible then
    RClient.Right := RClient.Right - FScrollBar.Width;

  DrawBoxBG(Buffer.Canvas, RClient, SkinManager, SkinSchemeIndex, True,
    MouseIn, isMouseDown, Enabled, Focused, 1, Color);

  Y := OffY;
  Buffer.Canvas.Font.Size := -1;
  Buffer.Canvas.Font := Font;
  for i:=FirstItem to Items.Count-1 do begin
    if not TSRVCBoxItem(Items.Items[i]).OpenParent then
      continue;
    ItemHeight := GetItemHeight(i);
    If Y + ItemHeight > RClient.Bottom then
      break;

    GetOffset;

    Buffer.Canvas.Pen.Color := clGray;

    if TSRVCBoxItem(Items.Items[i]).Selected then begin
      // draw ItemIndex
      Buffer.Canvas.Pen.Color := SelectedItemBorderColor;
      Buffer.Canvas.Brush.Color := SelectedItemBackColor;
      Buffer.Canvas.Font.Color := SelectedItemTextColor;
      ImgIndex := TSRVCBoxItem(Items.Items[i]).ImageIndex;
    end else if (MouseMoveIndex = i) then begin
      // draw select
      Buffer.Canvas.Pen.Color := HotItemBorderColor;
      Buffer.Canvas.Brush.Color := HotItemBackColor;
      Buffer.Canvas.Font.Color := HotItemTextColor;
      ImgIndex := TSRVCBoxItem(Items.Items[i]).SelectedImageIndex;
      If ImgIndex = -1 then
        ImgIndex := TSRVCBoxItem(Items.Items[i]).ImageIndex;
    end else begin
      // draw normal
      Buffer.Canvas.Pen.Color := TSRVCBoxItem(Items.Items[i]).BackColor;
      Buffer.Canvas.Brush.Color := Buffer.Canvas.Pen.Color;
      Buffer.Canvas.Font.Color := TSRVCBoxItem(Items.Items[i]).Color;
      ImgIndex := TSRVCBoxItem(Items.Items[i]).ImageIndex;
    end;

{    if (MouseMoveIndex = i) then begin
      // draw select
      Buffer.Canvas.Brush.Color := clHighlight;
      Buffer.Canvas.Font.Color := clWhite;
      ImgIndex := TSRVCBoxItem(Items.Items[i]).SelectedImageIndex;
      If ImgIndex = -1 then
        ImgIndex := TSRVCBoxItem(Items.Items[i]).ImageIndex;
    end else begin
      // draw normal
      ACanvas.Pen.Color := FSelectedItemBorderColor;
      ACanvas.Brush.Color := FSelectedItemBackColor;
      ACanvas.Font.Color := FSelectedItemTextColor;

      Buffer.Canvas.Pen.Color := TSRVCBoxItem(Items.Items[i]).BackColor;
      Buffer.Canvas.Brush.Color := Buffer.Canvas.Pen.Color;
      Buffer.Canvas.Font.Color := TSRVCBoxItem(Items.Items[i]).Color;
      ImgIndex := TSRVCBoxItem(Items.Items[i]).ImageIndex;
    end;}
    if FScrollBar.Visible then
      R := Rect(RClient.Left, Y, Round(Width / ScaleX) - FScrollBar.Width-1, Y+ItemHeight)
    else
      R := Rect(RClient.Left, Y, Round(Width / ScaleX)-1, Y+ItemHeight);

    DoSkin := DrawBoxBG(Buffer.Canvas, R, SkinManager, ItemSkinSchemeIndex, True,
      MouseMoveIndex = i, ItemIndex = i, Enabled, Focused, 1, clNone);

    if Buffer.Canvas.Pen.Color = clNone then
      Buffer.Canvas.Pen.Style := psClear;
    if Buffer.Canvas.Brush.Color = clNone then
      Buffer.Canvas.Brush.Style := bsClear;

    if not DoSkin then
      with R do
        Buffer.Canvas.Rectangle(Left, Top, Right, Bottom);

    Buffer.Canvas.Brush.Style := bsSolid;
    Buffer.Canvas.Pen.Style := psSolid;

    if UseItemFontNames then
      Buffer.Canvas.Font.Name := TSRVCBoxItem(Items.Items[i]).FontName;
    Buffer.Canvas.Font.Style := TSRVCBoxItem(Items.Items[i]).Style;

    if  not Assigned(FOnDrawItem) then begin
      // draw image LeftJustify
      If ((TSRVCBoxItem(Items.Items[i]).AlignImage = tlraLeftJustify) and (Step > 0)) or
         ((TSRVCBoxItem(Items.Items[i]).AlignImage = tlraRightJustify) and (Step < 0)) then
        DrawImage;

      // draw text
      if (srvioShowText in TSRVCBoxItem(Items.Items[i]).Option) then begin
        OffTextY := (ItemHeight - Buffer.Canvas.TextHeight('Text')) div 2;
        Buffer.Canvas.Brush.Style := bsClear;
        If Step > 0 then
          PosX := OffX + X + TSRVCBoxItem(Items.Items[i]).Indent
        else
          PosX := X - (OffX + TSRVCBoxItem(Items.Items[i]).Indent +
            Buffer.Canvas.TextWidth(TSRVCBoxItem(Items.Items[i]).Caption));

        Buffer.Canvas.TextOut(PosX, Y + OffTextY, TSRVCBoxItem(Items.Items[i]).Caption);
        X := X + (Buffer.Canvas.TextWidth(TSRVCBoxItem(Items.Items[i]).Caption) + 2) * Step;
      end;

      // draw image RightJustify
      If ((TSRVLBoxItem(Items.Items[i]).AlignImage = tlraRightJustify) and (Step > 0)) or
         ((TSRVLBoxItem(Items.Items[i]).AlignImage = tlraLeftJustify) and (Step < 0)) then
        DrawImage;
    end else
      if (MouseMoveIndex = i) then
        FOnDrawItem(Self, i, R, [odSelected])
      else
        FOnDrawItem(Self, i, R, []);

    DrawBoxBorder(Buffer.Canvas, R, SkinManager, ItemSkinSchemeIndex, True,
      MouseMoveIndex = i, ItemIndex = i, Enabled, Focused, 1);

    Y := Y + ItemHeight;
  end;

  FScrollBar.Height := RClient.Bottom;
  If FScrollBar.Visible then
    FScrollBar.PaintTo(Buffer.Canvas.Handle, RClient.Right, 0);

  DoSkin := DrawBoxBorder(Buffer.Canvas, RClient, SkinManager, SkinSchemeIndex, True,
    MouseIn, isMouseDown, Enabled, Focused, 1);

  If not DoSkin then begin
    Buffer.Canvas.Brush.Color := clBlack;
    Buffer.Canvas.FrameRect(RClient);
  end;

  SetViewportOrgEx(Buffer.Canvas.Handle, OldViewportOrg.cx, OldViewportOrg.cy, nil);
  SetViewportExtEx(Buffer.Canvas.Handle, OldViewportExt.cx, OldViewportExt.cy, nil);
  SetWindowOrgEx(Buffer.Canvas.Handle, OldWindowOrg.cx, OldWindowOrg.cy, nil);
  SetWindowExtEx(Buffer.Canvas.Handle, OldWindowExt.cx, OldWindowExt.cy, nil);
  SetMapMode(Buffer.Canvas.Handle, OldMapMode);

  If not DoSkin then begin
    Buffer.Canvas.Brush.Color := clBlack;
    Buffer.Canvas.FrameRect(ClientRect);
  end;

  Buffer.Canvas.Unlock;
end;

procedure TFSRVCBList.Paint;
begin
  If lockUpdate = 0 then begin
    Buffer.Canvas.CopyRect(ClientRect, Canvas, ClientRect);
    UpdateBuffer;
    Canvas.Draw(0, 0, Buffer);
  end;
end;

procedure TFSRVCBList.Invalidate;
begin
  if Assigned(Items) then
    Canvas.Draw(0, 0, Buffer);
end;

procedure TFSRVCBList.FormShow(Sender: TObject);
begin
//  Resize;
  Paint;
end;

procedure TFSRVCBList.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
     OldMouseMoveIndex, OldPosition : Integer;
begin
  MouseMoveIndex := CurItemIndex;
  OldMouseMoveIndex := MouseMoveIndex;

  if (Key = VK_RETURN) or (Key = VK_SPACE) and
     (MouseMoveIndex > -1) and (MouseMoveIndex < Items.Count) then begin
    BeginUpdate;
    if not (srvioCanSelect in TSRVCBoxItem(Items.Items[MouseMoveIndex]).Option) and
      (srvioCanHaveChildren in TSRVCBoxItem(Items.Items[MouseMoveIndex]).Option) then
      TSRVCBoxItem(Items.Items[MouseMoveIndex]).Expanded := not
        TSRVCBoxItem(Items.Items[MouseMoveIndex]).Expanded;
    if (srvioCanSelect in TSRVCBoxItem(Items.Items[MouseMoveIndex]).Option) then
      ItemIndex := MouseMoveIndex;

    OldPosition := FScrollBar.Position;
    Reset;
    FScrollBar.Position := OldPosition;
    FirstItem := GetItem(FScrollBar.Position);

    EndUpdate;
    if not (ssCtrl in Shift) then
      Close;
  end;

  if (Key = VK_F4) or (Key = VK_ESCAPE) then
    Close;

  if Key = VK_UP then
    if ssAlt in Shift then
      Close
    else
      MouseMoveIndex := Max(GetPriorItem(MouseMoveIndex, -1, False), 0);

  If (Key = VK_LEFT) then
    if (MouseMoveIndex > -1) and
        TSRVCBoxItem(Items.Items[MouseMoveIndex]).Expanded then
      TSRVCBoxItem(Items.Items[MouseMoveIndex]).Expanded := False
    else if ItemHasParent(MouseMoveIndex) then
      MouseMoveIndex := GetParent(MouseMoveIndex)
    else
      MouseMoveIndex := Max(GetPriorItem(MouseMoveIndex, -1, False), 0);

  If (Key = VK_RIGHT) then
    if (MouseMoveIndex > -1) and
        not TSRVCBoxItem(Items.Items[MouseMoveIndex]).Expanded then
      TSRVCBoxItem(Items.Items[MouseMoveIndex]).Expanded := True
    else
      MouseMoveIndex := Min(GetNextItem(MouseMoveIndex, 1, False), Items.Count-1);

  if Key = VK_DOWN then
    MouseMoveIndex := Min(GetNextItem(MouseMoveIndex, 1, False), Items.Count-1);

  if Key = VK_PRIOR then
    MouseMoveIndex := Max(GetPriorItem(MouseMoveIndex, -DropDownCount-1, False), 0);

  if Key = VK_NEXT then
    MouseMoveIndex := Min(GetNextItem(MouseMoveIndex, DropDownCount-1, False), Items.Count-1);

  if (Key = VK_HOME) then
    MouseMoveIndex := Min(GetNextItem(-1, 1, False), Items.Count-1);;

  if (Key = VK_END) then
    MouseMoveIndex := Max(GetPriorItem(Items.Count, -1, False), 0);

  if (MouseMoveIndex >= 0) and
     (not TSRVCBoxItem(Items.Items[MouseMoveIndex]).Enabled) then
    MouseMoveIndex := OldMouseMoveIndex;

  if (MouseMoveIndex <> -1) and
     ((MouseMoveIndex < FirstItem) or
      (MouseMoveIndex >= FirstItem + DropDownCount)) then begin
    if (MouseMoveIndex < FirstItem) then
      FirstItem := MouseMoveIndex;
    if (MouseMoveIndex >= FirstItem + DropDownCount) then
      FirstItem := MouseMoveIndex - DropDownCount + 1;

    FirstItem := Min(Max(FirstItem, 0), Items.Count - DropDownCount);
    FScrollBar.Position := FirstItem;
  end;
  If (MouseMoveIndex <> OldMouseMoveIndex) and
     (srvcbQuickKeySelect in Option) then
    SetItemIndex(MouseMoveIndex);
  Key := 0;
  CurItemIndex := MouseMoveIndex;
  Paint;
end;

function TFSRVCBList.GetTopItem(ItemIndex : Integer) : Integer;
var
     i : Integer;
begin
  Result := -1;
  if FirstItem <= ItemIndex then begin
    Result := 0;
    for i:= FirstItem to ItemIndex-1 do
      if TSRVCBoxItem(Items.Items[i]).OpenParent then
        Result := Result + GetItemHeight(ItemIndex);
  end;
end;

procedure TFSRVCBList.Reset;
var
     MaxPos, PageSize : Integer;
begin
  FScrollBar.Position := 0;
  FirstItem := 0;

  MaxPos := CalculateItemCount;
  PageSize := MaxPos - Max(CalculateItemCountToHeight(Round(Height / ScaleY)), 1);
  PageSize := Max(PageSize, MaxPos - DropDownCount);
  PageSize := Max(PageSize, 0);
  FScrollBar.PageSize := 0;
  FScrollBar.LargeChange := Max(Min(MaxPos div 10, 10), 1);
  FScrollBar.Max := MaxPos;
  FScrollBar.PageSize := ScrollBar.Max - PageSize + 1;

  FScrollBar.Visible := PageSize > 0;
  Paint;
end;

procedure TFSRVCBList.CMMouseEnter(var Message: TMessage);
begin
  inherited;
    MouseIn := True;
end;

procedure TFSRVCBList.CMMouseLeave(var Message: TMessage);
begin
  If IsByse then
    Exit;

  If not IsByse and FScrollBar.InMouse then begin
    IsByse := True;
    with Message do
      SendMessage(FScrollBar.Handle, CM_MOUSELEAVE, 0, 0);
    IsByse := False;
  end;

  inherited;

  MouseMoveIndex := -1;
  MouseIn := False;
  Paint;
end;

procedure TFSRVCBList.HookOnScroll(Sender: TObject; ScrollCode: TScrollCode;
  var ScrollPos: Integer);
begin
  FirstItem := GetItem(ScrollPos);
  Paint;
end;

{-----------------------------------------------------------------------}
procedure TFSRVCBList.WMRepaintControl(var Message: TMessage);
begin
  Paint;
end;

{-----------------------------------------------------------------------}
procedure TFSRVCBList.WMEraseBkgnd(var Message: TWMPaint);
begin
  Message.Result := 1;
end;

end.

