unit SRVListBox;

interface

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

type
  TSRVCustomListBox = class(TSRVWinControl)
  private
    { Private declarations }
    FScrollBar : TSRVScrollBar;
    FHideSelection : Boolean;
    FDoubleBuffered : Boolean;
    FShowCheckBoxes : Boolean;
    lockUpdate : Integer;
    FSorted : Boolean;
    FSelectedItemBorderColor : TColor;
    FSelectedItemBackColor : TColor;
    FSelectedItemTextColor : TColor;
    FHotItemBorderColor : TColor;
    FHotItemBackColor : TColor;
    FHotItemTextColor : TColor;
    FImageList : TCustomImageList;
    FUseItemFontNames : Boolean;
    FItemIndex : Integer;
    FItems : TSRVLBoxItemCollection;
    FItemHeight : Integer;
    FOnMeasureItem: TMeasureItemEvent;
    FOnDrawItem: TDrawItemEvent;
  protected
    { Protected declarations }
    isMouseDown : Boolean;
    isMouseMove : Boolean;
    IsByse : Boolean;

    FListMarginWidth : Integer;
    FListMarginHeight : Integer;
    FirstItem : Integer;
    AControl : TWinControl;
    Buffer : TBitmap;

    procedure UpdateBuffer(Canvas : TCanvas);
    function CalculateItemCountToHeight(Height : Integer) : Integer;
    function CalculateDrawItemCount(FirstItem : Integer) : Integer;
    function CalculateItemCount : Integer;
    function CalculateShowItemCount(FirstItem : Integer) : Integer;
    function CalculateDropItemCount(FirstItem : Integer) : Integer;
    procedure Reset;
    function CanChoice(Index : Integer; Shift: TShiftState) : Boolean;
    function GetItem(Position : Integer) : Integer;
    function GetPosition(ItemIndex : Integer) : Integer;
    procedure QsString(var PItems : array of TSRVLBoxItem; Count : Integer);
    procedure CheckOpenTree(Value: Integer);
    procedure ItemHint(Index : Integer);

    procedure WMDblClick(var Message: TWMMouse);  message WM_LBUTTONDBLCLK;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    function  DoMouseWheel(Shift: TShiftState; WheelDelta: Integer;
      MousePos: TPoint) : Boolean; override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure WMRepaintControl(var Message: TMessage); message WM_REPAINT_CONTROL;
    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
    procedure WMEraseBkgnd(var Message: TWMPaint); message WM_ERASEBKGND;
    procedure WMSize(var Message: TWMSize); message WM_SIZE;
    procedure WMSetFocus(var Message: TMessage); message WM_SETFOCUS;
    procedure WMKillFocus(var Message: TMessage); message WM_KILLFOCUS;
    procedure WMGetDlgCode(var Message: TMessage); message WM_GETDLGCODE;
    procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
    procedure HookOnAppend(Sender: TObject);
    procedure HookOnModify(Sender: TObject);
    procedure HookOnRemove(Sender: TObject);
    procedure HookOnScroll(Sender: TObject; ScrollCode: TScrollCode;
      var ScrollPos: Integer);

    function GetMultiSelect : Boolean;
    procedure SetMultiSelect(const Value : Boolean);
    procedure SetChecked(const Value : Boolean);
    procedure SetImageList(const Value : TCustomImageList);
    procedure SetUseItemFontNames(const Value : Boolean);
    procedure SetItemIndex(const Value : Integer);
    procedure SetItemHeight(const Value : Integer);
    procedure SetSelectedItemBorderColor(const Value : TColor);
    procedure SetSelectedItemBackColor(const Value : TColor);
    procedure SetSelectedItemTextColor(const Value : TColor);
    procedure SetHideSelection(Value: Boolean);
    procedure SetSorted(Value: Boolean);
    procedure SetDoubleBuffered(Value: Boolean);
    procedure SetSkinManager(Value : TSRVSkinManager);
    procedure SetScrollBarSkinSchemeIndex(Value : Integer);
    procedure SetListMarginWidth(Value : Integer);
    procedure SetListMarginHeight(Value : Integer);

    property SkinManager : TSRVSkinManager read FSkinManager write SetSkinManager;
    property  ScrollBarSkinSchemeIndex : Integer read FScrollBarSkinSchemeIndex write SetScrollBarSkinSchemeIndex default 0;
  public
    { Public declarations }
    MouseMoveIndex : Integer;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure Invalidate; override;
    procedure BeginUpdate;
    procedure EndUpdate;
    function CanUpdate : Boolean;
    function IndexOf(Value : String) : Integer;
    function ItemHasParent(Index : Integer) : Boolean;
    function ItemHasChild(Index : Integer) : Boolean;
    function GetParent(Index : Integer) : Integer;
    function GetPriorItem(Index, Delta : Integer; isMouse : Boolean) : Integer;
    function GetNextItem(Index, Delta : Integer; isMouse : Boolean) : Integer;
    function GetTopItem(ItemIndex : Integer) : Integer;
    function GetItemHeight(Index : Integer) : Integer;
    procedure AppendStrings(List : TStrings; UseIndent: Boolean);
    function AppendItem(Text : string;
                        ImageIndex, SelectedImageIndex, Indent : Integer;
                        Enabled, Checked : Boolean; Style: TFontStyles;
                        Color, BackColor : TColor; FontName : TFontName;
                        Expanded : Boolean; Option : TSRVItemOptions) : TSRVLBoxItem;
    function InsertItem(Index : Integer; Text : string;
                        ImageIndex, SelectedImageIndex, Indent : Integer;
                        Enabled, Checked : Boolean; Style: TFontStyles;
                        Color, BackColor : TColor; FontName : TFontName;
                        Expanded : Boolean; Option : TSRVItemOptions) : TSRVLBoxItem;
    procedure DeleteItem(Index : Integer);
    property ScrollBar : TSRVScrollBar read FScrollBar;
    property DoubleBuffered : Boolean read FDoubleBuffered write SetDoubleBuffered default False;
    property HideSelection : Boolean read FHideSelection write SetHideSelection default True;
    property Sorted : Boolean read FSorted write SetSorted default False;
    property MultiSelect : Boolean read GetMultiSelect write SetMultiSelect default False;
    property ShowCheckBoxes : Boolean read FShowCheckBoxes write SetChecked default False;
    property ListMarginWidth: Integer read FListMarginWidth write SetListMarginWidth default 3;
    property ListMarginHeight: Integer read FListMarginHeight write SetListMarginHeight default 2;
    property Items : TSRVLBoxItemCollection read FItems write FItems;
    property ImageList : TCustomImageList read FImageList write SetImageList;
    property UseItemFontNames : Boolean read FUseItemFontNames write SetUseItemFontNames;
    property ItemIndex : Integer read FItemIndex write SetItemIndex;
    property ItemHeight : Integer read FItemHeight write SetItemHeight;
    property HotItemBorderColor : TColor read FHotItemBorderColor write FHotItemBorderColor default clSilver;
    property HotItemBackColor : TColor read FHotItemBackColor write FHotItemBackColor default clBackground;
    property HotItemTextColor : TColor read FHotItemTextColor write FHotItemTextColor default clWhite;
    property SelectedItemBorderColor : TColor read FSelectedItemBorderColor write SetSelectedItemBorderColor default clSilver;
    property SelectedItemBackColor : TColor read FSelectedItemBackColor write SetSelectedItemBackColor default clHighlight;
    property SelectedItemTextColor : TColor read FSelectedItemTextColor write SetSelectedItemTextColor default clHighlightText;
    property OnMeasureItem: TMeasureItemEvent read FOnMeasureItem write FOnMeasureItem;
    property OnDrawItem: TDrawItemEvent read FOnDrawItem write FOnDrawItem;
  published
    { Published declarations }
  end;

  TSRVListBox = class(TSRVCustomListBox)
  private
    { Private declarations }
  protected
  public
    { Public declarations }
    property Text;
  published
    { Published declarations }
    property MultiSelect;
    property ShowCheckBoxes;
    property ListMarginWidth;
    property ListMarginHeight;
    property HotItemBorderColor;
    property HotItemBackColor;
    property HotItemTextColor;
    property SelectedItemBorderColor;
    property SelectedItemBackColor;
    property SelectedItemTextColor;
    property Sorted;
    property HideSelection;
    property SkinManager;
    property SkinSchemeIndex;
    property ScrollBarSkinSchemeIndex;
    property ItemSkinSchemeIndex;

    property AutoSize;
    property Anchors;
    property Align;
    property Hint;
    property Enabled;
    property Color default clWindow;
    property Width;
    property Height;
    property PopupMenu;
    property DoubleBuffered;
    property EnabledBorderColor;
    property DisabledBorderColor;
    property MouseInBorderColor;
    property FocusedBorderColor;
    property UseItemFontNames;
    property ShowHint;
    property Visible;
    property DrawOnPrint;
    property ImageList;
    property ItemHeight;
    property ItemIndex;
    property Items;
    property Font;
    property TabStop;
    property TabOrder;
    property OnDrawItem;
    property OnMeasureItem;
    property OnClick;
    property OnMouseMove;
    property OnMouseDown;
    property OnMouseUp;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnEnter;
    property OnExit;
    property OnDblClick;
    {$IFDEF RICHVIEWDEF5}
    property OnContextPopup;
    {$ENDIF}
    property OnMouseEnter;
    property OnMouseLeave;
  end;

implementation
uses Math;

function TSRVCustomListBox.IndexOf(Value : String) : Integer;
var
     i : Integer;
begin
  Result := -1;
  for i:=0 to Items.Count-1 do
    if TSRVLBoxItem(Items.Items[i]).Caption = Value then begin
      Result := i;
      break;
    end;
end;


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

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

procedure TSRVCustomListBox.AppendStrings(List : TStrings;
  UseIndent: Boolean);
var
     index, Indent : Integer;
     Text : String;
begin
  BeginUpdate;
  Indent := 0;
  for index:=0 to List.Count-1 do begin
    Text := List.Strings[index];
    if UseIndent then begin
      Indent := Length(Text) - Length(TrimLeft(Text));
      // If Tab
      while Text[1] = #9 do begin
        Indent := Indent + 10;
        Text := Copy(Text, 2, Length(Text) - 1);
      end;
    end;
    if UseIndent then
      if Indent = 0 then
        AppendItem(Text, -1, -1, Indent, True, False, [], Font.Color, Color,
           Font.Name, False, [srvioShowCheckBox, srvioShowImage, srvioShowText, srvioCanHaveChildren])
      else
        AppendItem(Text, -1, -1, Indent, True, False, [], Font.Color, Color,
           Font.Name, True, [srvioShowCheckBox, srvioShowImage, srvioShowText, srvioCanSelect, srvioCanHaveChildren])
    else
      AppendItem(Text, -1, -1, Indent, True, False, [], Font.Color, Color,
        Font.Name, True, [srvioShowCheckBox, srvioShowImage, srvioShowText, srvioCanSelect, srvioCanHaveChildren]);
  end;
  EndUpdate;
  Reset;
end;

function TSRVCustomListBox.AppendItem(Text : string;
                            ImageIndex, SelectedImageIndex, Indent : Integer;
                            Enabled, Checked : Boolean; Style: TFontStyles;
                            Color, BackColor : TColor; FontName : TFontName;
                            Expanded : Boolean; Option : TSRVItemOptions) : TSRVLBoxItem;
var
     Item : TSRVLBoxItem;
begin
  BeginUpdate;
  Item := TSRVLBoxItem(FItems.Add);
  Item.Caption := Text;
  Item.ImageIndex := ImageIndex;
  Item.SelectedImageIndex := SelectedImageIndex;
  Item.Indent := Indent;
  Item.Enabled := Enabled;
  Item.Checked := Checked;
  Item.Style := Style;
  Item.Color := Color;
  Item.BackColor := BackColor;
  Item.FontName := FontName;
  Item.Option := Option;
  Item.Expanded := Expanded;
  if FSorted then
    Sorted := Sorted;
  Result := Item;
  EndUpdate;
  Reset;
end;

function TSRVCustomListBox.InsertItem(Index : Integer; Text : string;
                            ImageIndex, SelectedImageIndex, Indent : Integer;
                            Enabled, Checked : Boolean; Style: TFontStyles;
                            Color, BackColor : TColor; FontName : TFontName;
                            Expanded : Boolean; Option : TSRVItemOptions) : TSRVLBoxItem;
var
     Item : TSRVLBoxItem;
begin
  BeginUpdate;
  Item := TSRVLBoxItem(FItems.Insert(Index));
  Item.Caption := Text;
  Item.ImageIndex := ImageIndex;
  Item.SelectedImageIndex := SelectedImageIndex;
  Item.Indent := Indent;
  Item.Enabled := Enabled;
  Item.Checked := Checked;
  Item.Style := Style;
  Item.Color := Color;
  Item.BackColor := BackColor;
  Item.FontName := FontName;
  Item.Option := Option;
  Item.Expanded := Expanded;
  if FSorted then
    Sorted := Sorted;
  Result := Item;
  EndUpdate;
  Reset;
end;

procedure TSRVCustomListBox.DeleteItem(Index : Integer);
begin
  FItems.Items[Index].Free;
  if ItemIndex = FItems.Count then
    ItemIndex := FItems.Count - 1;
  Reset;
end;

constructor TSRVCustomListBox.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  isMouseDown := False;
  isMouseMove := False;
  IsByse := False;
//  NewStyleControls := False;

  Buffer := nil;
  FSorted := False;
  FDoubleBuffered := False;
  lockUpdate := 0;
  FirstItem := 0;

  FListMarginWidth := 3;
  FListMarginHeight := 2;
  FShowCheckBoxes := False;
  FImageList := nil;
  FHideSelection := True;
  FUseItemFontNames := False;
  FItemIndex := -1;
  MouseMoveIndex := -1;
  FItemHeight := 22;
  FEnabledBorderColor := SRV_BORDER_ENABLED;
  FDisabledBorderColor := SRV_BORDER_DISABLED;
  FMouseInBorderColor := SRV_BORDER_MOUSEIN;
  FFocusedBorderColor := SRV_BORDER_FOCUSED;
  FSelectedItemBorderColor := clSilver;
  FSelectedItemBackColor := clHighlight;
  FSelectedItemTextColor := clHighlightText;
  FHotItemBorderColor := clSilver;
  FHotItemBackColor := clBackground;
  FHotItemTextColor := clWhite;

  FScrollBar := TSRVScrollBar.Create(nil);
  FScrollBar.Parent := Self;
  FScrollBar.Kind := sbVertical;
  FScrollBar.Width := GetSystemMetrics(SM_CXVSCROLL);
//  FScrollBar.Align := alRight;
  FScrollBar.SmallChange := 1;
  FScrollBar.LargeChange := 5;
  FScrollBar.TabStop := False;
  FScrollBar.OnScroll := HookOnScroll;
  FScrollBar.Enabled := True;
  FScrollBar.Visible := False;

  TabStop := True;

  Text := Name;
  FItems := TSRVLBoxItemCollection.Create(TSRVLBoxItem, Self);
  FItems.OnAppend := HookOnAppend;
  FItems.OnModify := HookOnModify;
  FItems.OnRemove := HookOnRemove;
  MultiSelect := False;

  Height := 180;
  Width := 180;
  Color := clWindow;
end;

destructor TSRVCustomListBox.Destroy;
begin
  FItems.Destroy;
  FScrollBar.Destroy;
  FScrollBar := nil;
  If Assigned(Buffer) then
    Buffer.Free;
  inherited;
end;

function TSRVCustomListBox.GetItemHeight(Index : Integer) : Integer;
var
     Canvas : TCanvas;
     DC : HDC;
begin
  Result := FItemHeight;
  if Index = -1 then
    exit;
  Canvas := TCanvas.Create;
  DC := GetWindowDC(Handle);
  Canvas.Handle := DC;
  Canvas.Font.Assign(Font);
  if UseItemFontNames then
    Canvas.Font.Name := TSRVLBoxItem(Items.Items[Index]).FontName;
  Canvas.Font.Style := TSRVLBoxItem(Items.Items[Index]).Style;
  FItemHeight := Canvas.TextHeight(TSRVLBoxItem(Items.Items[Index]).Caption);
  ReleaseDC(Handle, DC);
  Canvas.Handle := 0;
  Canvas.Free;
  if Assigned(ImageList) then
    FItemHeight := Max(FItemHeight, ImageList.Height);
  FItemHeight := Max(FItemHeight + FListMarginHeight, 16);
  if (srvioCanHaveChildren in TSRVLBoxItem(Items.Items[Index]).Option) and
     not TSRVLBoxItem(Items.Items[Index]).OpenParent then
    FItemHeight := 0;

  Result := FItemHeight;
  if Assigned(FOnMeasureItem) then
    FOnMeasureItem(AControl, Index, Result);
end;

procedure TSRVCustomListBox.UpdateBuffer(Canvas : TCanvas);
var
  i, X, Y, OffImgY, OffTextY, ItemHeight, ImgIndex, PosX, Step : Integer;
  State: Cardinal;
  R, RItem, RClient : TRect;
  DoSkin : Boolean;

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

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

          if FShowCheckBoxes and
            (srvioShowCheckBox in TSRVLBoxItem(Items.Items[i]).Option) then
            PosX := PosX + (16 + 2);

          if (srvioShowText in TSRVLBoxItem(Items.Items[i]).Option) then begin
            if UseItemFontNames then
              Canvas.Font.Name := TSRVLBoxItem(Items.Items[i]).FontName;
            Canvas.Font.Style := TSRVLBoxItem(Items.Items[i]).Style;
            PosX := PosX + (Canvas.TextWidth(TSRVLBoxItem(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(ImageList) and
         (srvioShowImage in TSRVLBoxItem(Items.Items[i]).Option) then begin
        OffImgY := (ItemHeight - ImageList.Height) div 2;
        If Step > 0 then
          PosX := X + FListMarginWidth + TSRVLBoxItem(Items.Items[i]).Indent
        else
          PosX := X - FListMarginWidth - TSRVLBoxItem(Items.Items[i]).Indent -
            ImageList.Width;
        ImageList.Draw(Canvas, PosX, Y + OffImgY, ImgIndex);
        X := X + (ImageList.Width + 2) * Step;
      end;
  end;

  procedure DrawCheckBox;
  begin
      // draw checkbox
      if FShowCheckBoxes and
         (srvioShowCheckBox in TSRVLBoxItem(Items.Items[i]).Option) then begin
        OffImgY := (ItemHeight - 16) div 2;
        If Step > 0 then
          PosX := X + FListMarginWidth + TSRVLBoxItem(Items.Items[i]).Indent
        else
          PosX := X - FListMarginWidth - TSRVLBoxItem(Items.Items[i]).Indent - 16;
        r := Bounds(PosX, Y + OffImgY, 16, 16);

        State := 0;
        case TSRVLBoxItem(Items.Items[i]).State of
        cbChecked   :
          State := DFCS_CHECKED;
        cbGrayed    :
          State := DFCS_CHECKED or DFCS_INACTIVE;
//          State := DFCS_CHECKED or DFCS_PUSHED;
        end;

        DrawFrameControl(Canvas.Handle, r, DFC_BUTTON, DFCS_BUTTONCHECK or State or DFCS_FLAT);
        X := X + (16 + 2) * Step;
      end;
  end;

begin
  Canvas.Lock;

  If FScrollBar.Visible then
    RClient := Bounds(0, 0, Width-FScrollBar.Width, Height)
  else
    RClient := Bounds(0, 0, Width, Height);

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

  Y := 1;
  Canvas.Font.Size := -1;
  Canvas.Font := Font;
  for i:=FirstItem to Items.Count-1 do begin
    if not TSRVLBoxItem(Items.Items[i]).OpenParent then
      continue;
    ItemHeight := GetItemHeight(i);
    if (Y > Height) then begin
      Canvas.Brush.Style := bsClear;
      Break;
    end;
    Canvas.Font.Assign(Font);
    GetOffset;
    if ((Focused and FHideSelection) or not FHideSelection) and
       TSRVLBoxItem(Items.Items[i]).Selected then begin
      // draw ItemIndex
      Canvas.Pen.Color := FSelectedItemBorderColor;
      Canvas.Brush.Color := FSelectedItemBackColor;
      Canvas.Font.Color := FSelectedItemTextColor;
      ImgIndex := TSRVLBoxItem(Items.Items[i]).ImageIndex;
    end else if (MouseMoveIndex = i) then begin
      // draw select
      Canvas.Pen.Color := FHotItemBorderColor;
      Canvas.Brush.Color := FHotItemBackColor;
      Canvas.Font.Color := FHotItemTextColor;
      ImgIndex := TSRVLBoxItem(Items.Items[i]).SelectedImageIndex;
      If ImgIndex = -1 then
        ImgIndex := TSRVLBoxItem(Items.Items[i]).ImageIndex;
    end else begin
      // draw normal
      Canvas.Pen.Color := TSRVLBoxItem(Items.Items[i]).BackColor;
      Canvas.Brush.Color := Canvas.Pen.Color;
      Canvas.Font.Color := TSRVLBoxItem(Items.Items[i]).Color;
      ImgIndex := TSRVLBoxItem(Items.Items[i]).ImageIndex;
    end;

    if ScrollBar.Visible then
      RItem := Bounds(1, Y, Width - ScrollBar.Width-1, ItemHeight)
    else
      RItem := Bounds(1, Y, Width - 1, ItemHeight);

    DoSkin := DrawBoxBG(Canvas, RItem, SkinManager, ItemSkinSchemeIndex, True,
      MouseMoveIndex = i, ItemIndex = i,
      Enabled and TSRVLBoxItem(Items.Items[i]).Enabled,
      Focused, 1, clNone);

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

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

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

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

    if  not Assigned(FOnDrawItem) then begin
      // draw CheckBox LeftJustify
      If ((TSRVLBoxItem(Items.Items[i]).AlignCheckBox = tlraLeftJustify) and (Step > 0)) or
         ((TSRVLBoxItem(Items.Items[i]).AlignCheckBox = tlraRightJustify) and (Step < 0)) then
        DrawCheckBox;

      // draw image LeftJustify
      If ((TSRVLBoxItem(Items.Items[i]).AlignImage = tlraLeftJustify) and (Step > 0)) or
         ((TSRVLBoxItem(Items.Items[i]).AlignImage = tlraRightJustify) and (Step < 0)) then
        DrawImage;
      // draw text
      if (srvioShowText in TSRVLBoxItem(Items.Items[i]).Option) then begin
        OffTextY := (ItemHeight - Canvas.TextHeight('Text')) div 2;
        Canvas.Brush.Style := bsClear;
        If Step > 0 then
          PosX := FListMarginWidth + X + TSRVLBoxItem(Items.Items[i]).Indent
        else
          PosX := X - (FListMarginWidth + TSRVLBoxItem(Items.Items[i]).Indent +
            Canvas.TextWidth(TSRVLBoxItem(Items.Items[i]).Caption));

        Canvas.TextOut(PosX, Y + OffTextY, TSRVLBoxItem(Items.Items[i]).Caption);
        X := X + (Canvas.TextWidth(TSRVLBoxItem(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;
      // draw CheckBox LeftJustify
      If ((TSRVLBoxItem(Items.Items[i]).AlignCheckBox = tlraRightJustify) and (Step > 0)) or
         ((TSRVLBoxItem(Items.Items[i]).AlignCheckBox = tlraLeftJustify) and (Step < 0)) then
        DrawCheckBox;
    end else
      if (MouseMoveIndex = i) then
        FOnDrawItem(Self, i, RItem, [odSelected])
      else
        FOnDrawItem(Self, i, RItem, []);

    DrawBoxBorder(Canvas, RItem, SkinManager, ItemSkinSchemeIndex, True,
      MouseMoveIndex = i, ItemIndex = i,
      Enabled and TSRVLBoxItem(Items.Items[i]).Enabled, Focused, 1);

    Y := Y + ItemHeight;
  end;

  FScrollBar.ParentSRV := ParentSRV;
  FScrollBar.Printing := Printing;
  FScrollBar.ScaleX := ScaleX;
  FScrollBar.ScaleY := ScaleY;
  FScrollBar.ScreenOffX := ScreenOffX + Round(FScrollBar.Left * ScaleX);
  FScrollBar.ScreenOffY := ScreenOffY - Round(FScrollBar.Top * ScaleY);
  FScrollBar.CanvasOffX := CanvasOffX + FScrollBar.Left;
  FScrollBar.CanvasOffY := CanvasOffY + FScrollBar.Top;
  FScrollBar.MouseOffX_ := FScrollBar.Left + Round((ScreenOffX - FScrollBar.ClientToScreen(Point(0, 0)).x) / ScaleX);
  FScrollBar.MouseOffY_ := FScrollBar.Top + Round((ScreenOffY - FScrollBar.ClientToScreen(Point(0, 0)).y) / ScaleY);

  FScrollBar.IsSkin := True;
  If not ParentSRV and FScrollBar.Visible then
    FScrollBar.PaintTo(Canvas.Handle, RClient.Right, 0);

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

  If not DoSkin then
  if Enabled = True then
    if MouseIn then
      Frame3D(Canvas, RClient, MouseInBorderColor, MouseInBorderColor, MouseInBorderWidth)
    else if Focused then
      Frame3D(Canvas, RClient, FocusedBorderColor, FocusedBorderColor, FocusedBorderWidth)
    else
      Frame3D(Canvas, RClient, EnabledBorderColor, EnabledBorderColor, EnabledBorderWidth)
  else
    Frame3D(Canvas, RClient, DisabledBorderColor, DisabledBorderColor, DisabledBorderWidth);

  Canvas.Unlock;
end;

function TSRVCustomListBox.CanChoice(Index : Integer; Shift: TShiftState) : Boolean;
var
     i : Integer;
begin
  Result := True;
  if (Index = -1) or not ((ssShift in Shift) or (ssCtrl in Shift)) then
    Exit;
  // SHIFT + Click
  BeginUpdate;
  if (ssShift in Shift) and (ItemIndex <> -1) then begin
    Items.ClearSelect;
    for i:= Min(ItemIndex, Index) to Max(ItemIndex, Index) do
      TSRVLBoxItem(Items.Items[i]).Selected := True;
    Result := False;
  end;
  // CTRL + Click
  if (ssCtrl in Shift) then begin
    TSRVLBoxItem(Items.Items[Index]).Selected :=
      not TSRVLBoxItem(Items.Items[Index]).Selected;
    Result := False;
  end;
  EndUpdate;
end;

procedure TSRVCustomListBox.WMDblClick(var Message: TWMMouse);
var
     X, Y, OffImgY : Integer;
begin
  inherited;

  isMouseDown := not (FScrollBar.Visible and (Message.XPos >= Width-1 - FScrollBar.Width));
  if isMouseDown and (MouseMoveIndex > -1) and (MouseMoveIndex < Items.Count) then begin
    OffImgY := (GetItemHeight(MouseMoveIndex) - 16) div 2;
    X := Message.XPos - (1 + FListMarginWidth + TSRVLBoxItem(Items.Items[MouseMoveIndex]).Indent);
    Y := Message.YPos - (GetTopItem(MouseMoveIndex) + OffImgY);
    if not (FShowCheckBoxes and
       (srvioShowCheckBox in TSRVLBoxItem(Items.Items[MouseMoveIndex]).Option) and
       (X >= 0) and (X <= 16) and (Y >= 0) and (Y <= 16)) and
      (srvioCanSelect in TSRVLBoxItem(Items.Items[MouseMoveIndex]).Option) then
        TSRVLBoxItem(Items.Items[MouseMoveIndex]).Expanded := not
          TSRVLBoxItem(Items.Items[MouseMoveIndex]).Expanded;
  end;

  Repaint;
end;

procedure TSRVCustomListBox.MouseDown(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer);
var
     OffImgY : Integer;
     Message: TWMMouse;
begin
  if (csDesigning in ComponentState) then
    exit;
  // ScrollBar
  if not Visible then exit;
  SetFocus;
  isMouseDown := not (FScrollBar.Visible and (X >= Width-1 - FScrollBar.Width));
  if not isMouseDown then begin
    // in scrollbar
    Message.XPos := X - (Width - FScrollBar.Width-1);
    Message.YPos := Y - FScrollBar.Top;
    with TMessage(Message) do
      PostMessage(FScrollBar.Handle, WM_LBUTTONDOWN, WParam, LParam);
    Repaint;
    Exit;
  end else if (MouseMoveIndex > -1) and (MouseMoveIndex < Items.Count) then begin
    // checkbox
    OffImgY := (GetItemHeight(MouseMoveIndex) - 16) div 2;
    X := X - (1 + FListMarginWidth + TSRVLBoxItem(Items.Items[MouseMoveIndex]).Indent);
    Y := Y - (GetTopItem(MouseMoveIndex) + OffImgY);
    if FShowCheckBoxes and
       (srvioShowCheckBox in  TSRVLBoxItem(Items.Items[MouseMoveIndex]).Option) then begin
      if (X >= 0) and (X <= 16) and (Y >= 0) and (Y <= 16) then
        TSRVLBoxItem(Items.Items[MouseMoveIndex]).Checked :=
          not TSRVLBoxItem(Items.Items[MouseMoveIndex]).Checked
      else if (srvioCanHaveChildren in TSRVLBoxItem(Items.Items[MouseMoveIndex]).Option) then begin
        // Click
        if CanChoice(MouseMoveIndex, Shift) and
           (srvioCanSelect in TSRVLBoxItem(Items.Items[MouseMoveIndex]).Option) then
          ItemIndex := MouseMoveIndex;

        if not (srvioCanSelect in TSRVLBoxItem(Items.Items[MouseMoveIndex]).Option) then
          TSRVLBoxItem(Items.Items[MouseMoveIndex]).Expanded := not
            TSRVLBoxItem(Items.Items[MouseMoveIndex]).Expanded;
      end;
    end else begin
      // Items
      if CanChoice(MouseMoveIndex, Shift) and
         (srvioCanSelect in TSRVLBoxItem(Items.Items[MouseMoveIndex]).Option) then
        ItemIndex := MouseMoveIndex;

      if not (srvioCanSelect in TSRVLBoxItem(Items.Items[MouseMoveIndex]).Option) then
        TSRVLBoxItem(Items.Items[MouseMoveIndex]).Expanded := not
          TSRVLBoxItem(Items.Items[MouseMoveIndex]).Expanded;
    end;
  end;

  Repaint;
  inherited;
end;

function TSRVCustomListBox.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 TSRVLBoxItem(Items.Items[i]).OpenParent then
        Result := Result + GetItemHeight(ItemIndex);
  end;
end;

procedure TSRVCustomListBox.MouseMove(Shift: TShiftState; X, Y: Integer);
var
     ItemH, yItem, index, OldMouseMoveIndex : Integer;
     Message: TWMMouse;
begin
  if (csDesigning in ComponentState) then
    exit;

  if FScrollBar.Visible and (X >= Width-1 - FScrollBar.Width) then begin
    // in scrollbar
    Message.XPos := X - (Width - FScrollBar.Width-1);
    Message.YPos := Y - FScrollBar.Top;
    with TMessage(Message) do
      PostMessage(FScrollBar.Handle, WM_MOUSEMOVE, WParam, LParam);
    Repaint;
    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)-1 do begin
      if not TSRVLBoxItem(Items.Items[index]).OpenParent then
        continue;
      ItemH := GetItemHeight(index);
      if (Y >= yItem) and (Y < 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 TSRVLBoxItem(Items.Items[MouseMoveIndex]).Enabled then
      MouseMoveIndex := OldMouseMoveIndex;
  end;
  Repaint;
  ItemHint(MouseMoveIndex);
  inherited;
end;

procedure TSRVCustomListBox.MouseUp(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
begin
  isMouseDown := False;
  inherited;
end;

function  TSRVCustomListBox.DoMouseWheel(Shift: TShiftState; WheelDelta: Integer;
      MousePos: TPoint) : Boolean;
begin
  inherited DoMouseWheel(Shift, WheelDelta, MousePos);

  If ScrollBar.Visible then begin
    BeginUpdate;
    if WheelDelta <= 0 then
      ScrollBar.Position := ScrollBar.Position + Mouse.WheelScrollLines
    else
      ScrollBar.Position := ScrollBar.Position - Mouse.WheelScrollLines;
    FirstItem := Max(GetItem(ScrollBar.Position), 0);
    EndUpdate;
  end;
  Repaint;

  Result := True;
end;

procedure TSRVCustomListBox.KeyDown(var Key: Word; Shift: TShiftState);
var
     OldMouseMoveIndex, OldPosition : Integer;
begin
  MouseMoveIndex := ItemIndex;
  OldMouseMoveIndex := MouseMoveIndex;

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

    OldPosition := ScrollBar.Position;
    Reset;
    ScrollBar.Position := OldPosition;
    FirstItem := Max(GetItem(ScrollBar.Position), 0);

    EndUpdate;
    Exit;
  end;

  If (Key = VK_LEFT) then
    if (MouseMoveIndex > -1) and
        TSRVLBoxItem(Items.Items[MouseMoveIndex]).Expanded and
        ItemHasChild(MouseMoveIndex) then
      TSRVLBoxItem(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 TSRVLBoxItem(Items.Items[MouseMoveIndex]).Expanded and
        ItemHasChild(MouseMoveIndex) then
      TSRVLBoxItem(Items.Items[MouseMoveIndex]).Expanded := True
    else
      MouseMoveIndex := Min(GetNextItem(MouseMoveIndex, 1, False), Items.Count-1);

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

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

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

  if Key = VK_NEXT then
    MouseMoveIndex := Min(GetNextItem(MouseMoveIndex, CalculateDrawItemCount(FirstItem), 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 TSRVLBoxItem(Items.Items[MouseMoveIndex]).Enabled) then
    MouseMoveIndex := OldMouseMoveIndex;

  If MouseMoveIndex <> OldMouseMoveIndex then
    CheckOpenTree(MouseMoveIndex);

  if (OldMouseMoveIndex <> MouseMoveIndex) then
    if (ssShift in Shift) then
      CanChoice(MouseMoveIndex, Shift)
    else if not (ssCtrl in Shift) then
      ItemIndex := MouseMoveIndex;

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

    FirstItem := Max(Min(FirstItem, Items.Count - CalculateDropItemCount(FirstItem)), 0);
    ScrollBar.Position := GetPosition(FirstItem);
  end;
  Key := 0;

  If ParentSRV and (Parent <> nil) and (Parent.ClassName <> 'TSRichViewEdit') then
    PostMessage(Parent.Handle, WM_REPAINT_CONTROL, 0, 0)
  else Repaint;

  inherited;
end;

procedure TSRVCustomListBox.Invalidate;
begin
  If lockUpdate <> 0 then
    exit;
{  If Parent <> nil then begin
    SendMessage(Handle, WM_PAINT, 0, 0);
  end else}
    inherited;
end;

procedure TSRVCustomListBox.WMPaint(var Message: TWMPaint);
var
     cbRect : TRect;
     Canvas : TCanvas;
     DC : HDC;
     lpPaint : tagPAINTSTRUCT;
begin
  Canvas := TCanvas.Create;
  if Message.DC = 0 then
    DC := BeginPaint(Handle, lpPaint)
  else
    DC := Message.DC;
  Canvas.Handle := DC;
  GetWindowRect(Handle, cbRect);
  OffsetRect(cbRect, -cbRect.Left, -cbRect.Top);

  if DoubleBuffered then begin
    UpdateBuffer(Buffer.Canvas);
    Canvas.Draw(0, 0, Buffer);
  end else
    UpdateBuffer(Canvas);

  Canvas.Handle := 0;
  Canvas.Free;

  if Message.DC = 0 then
    EndPaint(Handle, lpPaint);
end;

procedure TSRVCustomListBox.WMSize(var Message: TWMSize);
begin
  inherited;
  SendMessage(Handle, LB_SETCARETINDEX, -1, 0);
  If Assigned(Buffer) then begin
    Buffer.Width := Round(Width * ScaleX);
    Buffer.Height := Round(Height * ScaleY);
  end;
  Reset;
end;

procedure TSRVCustomListBox.Reset;
var
     MaxPos, PageSize : Integer;
begin
  ScrollBar.Position := 0;
  FirstItem := 0;

  MaxPos := CalculateItemCount;
  PageSize := MaxPos - Max(CalculateItemCountToHeight(Height), 1);
  PageSize := Max(PageSize, 0);
  ScrollBar.PageSize := 0;
  ScrollBar.Max := MaxPos;
  ScrollBar.PageSize := ScrollBar.Max - PageSize + 1;

  if (csDesigning in ComponentState) then
    If PageSize > 0 then
      ScrollBar.Align := alRight
    else begin
      ScrollBar.Align := alNone;
      ScrollBar.Left := -ScrollBar.Width;
    end
  else begin
    ScrollBar.Visible := PageSize > 0;
    ScrollBar.Height := Height;
    If ParentSRV then
      ScrollBar.Left := Width - FScrollBar.Width
    Else
      ScrollBar.Left := -FScrollBar.Width;
  end;

  Repaint;
end;

function TSRVCustomListBox.CalculateShowItemCount(FirstItem : Integer) : Integer;
var
     i, H, Y : Integer;
begin
  Result := Items.Count;
  Y := FListMarginHeight;
  for i:=FirstItem to Items.Count-1 do begin
    if not TSRVLBoxItem(Items.Items[i]).OpenParent then
      continue;
    H := GetItemHeight(i);
    if (Y > Height - ListMarginHeight) then begin
      Result := i;
      Break;
    end;
    Y := Y + H;
  end;
end;

function TSRVCustomListBox.CalculateDropItemCount(FirstItem : Integer) : Integer;
var
     i, H, Y : Integer;
begin
  Result := 0;
  Y := FListMarginHeight;
  for i:=FirstItem to Items.Count-1 do begin
    H := GetItemHeight(i);
    Y := Y + H;
    if (Y > Height - ListMarginHeight) then
      Break;
    Result := Result + 1;
  end;
end;

function TSRVCustomListBox.CalculateItemCount : Integer;
var
     i : Integer;
begin
  Result := 0;
  for i:= 0 to Items.Count-1 do begin
    if GetItemHeight(i) > 0 then
      Result := Result + 1;
  end;
end;

function TSRVCustomListBox.CalculateDrawItemCount(FirstItem : Integer) : Integer;
var
     i, H, Y : Integer;
begin
  Result := 0;
  Y := FListMarginHeight;
  for i:=FirstItem to Items.Count-1 do begin
    H := GetItemHeight(i);
    Y := Y + H;
    if (Y > Height - ListMarginHeight) then
      Break;
    if H > 0 then
      Result := Result + 1;
  end;
end;

procedure TSRVCustomListBox.CMMouseEnter(var Message: TMessage);
begin
  inherited;
  Repaint;
end;

procedure TSRVCustomListBox.CMMouseLeave(var Message: TMessage);
begin
  If IsByse and ParentSRV then
    Exit;

  inherited;

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

  MouseMoveIndex := -1;
  isMouseMove := False;
  Repaint;
end;


procedure TSRVCustomListBox.WMSetFocus(var Message: TMessage);
begin
  inherited;
  Repaint;
end;

procedure TSRVCustomListBox.WMKillFocus(var Message: TMessage);
begin
  inherited;
  Repaint;
end;

procedure TSRVCustomListBox.HookOnAppend(Sender: TObject);
var
     OldPosition : Integer;
begin
  if lockUpdate <> 0 then exit;
  OldPosition := ScrollBar.Position;
  Reset;
  ScrollBar.Position := OldPosition;
  FirstItem := Max(GetItem(ScrollBar.Position), 0);
  Repaint;
end;

procedure TSRVCustomListBox.HookOnModify(Sender: TObject);
var
     OldPosition : Integer;
begin
  if lockUpdate <> 0 then exit;
  OldPosition := ScrollBar.Position;
  Reset;
  ScrollBar.Position := OldPosition;
  FirstItem := Max(GetItem(ScrollBar.Position), 0);

  Repaint;
end;

procedure TSRVCustomListBox.HookOnRemove(Sender: TObject);
var
     OldPosition : Integer;
begin
  if lockUpdate <> 0 then exit;
  OldPosition := ScrollBar.Position;
  Reset;
  ScrollBar.Position := OldPosition;
  FirstItem := Max(GetItem(ScrollBar.Position), 0);

  if (MouseMoveIndex >= Items.Count) then
    MouseMoveIndex := -1;

  if (ItemIndex >= Items.Count) then
    ItemIndex := -1;

  Repaint;
end;

procedure TSRVCustomListBox.BeginUpdate;
begin
  Inc(lockUpdate);
end;

procedure TSRVCustomListBox.EndUpdate;
begin
  Dec(lockUpdate);
end;

function TSRVCustomListBox.CanUpdate : Boolean;
begin
  Result := lockUpdate = 0;
end;

function TSRVCustomListBox.GetMultiSelect : Boolean;
begin
  if Assigned(Items) then
    Result := Items.MultiSelect
  else
    Result := False;
end;

procedure TSRVCustomListBox.SetMultiSelect(const Value : Boolean);
begin
  if Assigned(Items) then
    Items.MultiSelect := Value;
end;

procedure TSRVCustomListBox.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (Operation = opRemove) and (AComponent = FImageList) then
    FImageList := nil;

  if (Operation = opRemove) and (AComponent = FSkinManager) then begin
    FSkinManager := nil;
    If Assigned(FScrollBar) then
      FScrollBar.SkinManager := nil;
  end;
end;

procedure TSRVCustomListBox.SetChecked(const Value : Boolean);
begin
  FShowCheckBoxes := Value;
  Repaint;
end;

procedure TSRVCustomListBox.SetImageList(const Value : TCustomImageList);
var
     OldPosition : Integer;
begin
  FImageList := Value;
  OldPosition := ScrollBar.Position;
  Reset;
  ScrollBar.Position := OldPosition;
  FirstItem := Max(GetItem(ScrollBar.Position), 0);
  Repaint;
end;

procedure TSRVCustomListBox.SetUseItemFontNames(const Value : Boolean);
begin
  FUseItemFontNames := Value;
  Repaint;
end;

procedure TSRVCustomListBox.SetItemIndex(const Value : Integer);
begin
  if (Value < -1) or (Value >= Items.Count) then Exit;

  BeginUpdate;
  Items.ClearSelect;
  FItemIndex := Value;
  if (Value >= 0) and (Value < Items.Count) then
    TSRVLBoxItem(Items.Items[Value]).Selected := True;
  EndUpdate;
  Repaint;
end;

procedure TSRVCustomListBox.SetItemHeight(const Value : Integer);
begin
  FItemHeight := Max(Value, 0);
  Repaint;
end;

procedure TSRVCustomListBox.SetSelectedItemBorderColor(const Value : TColor);
begin
  FSelectedItemBorderColor := Value;
  Repaint;
end;

procedure TSRVCustomListBox.SetSelectedItemBackColor(const Value : TColor);
begin
  FSelectedItemBackColor := Value;
  Repaint;
end;

procedure TSRVCustomListBox.SetSelectedItemTextColor(const Value : TColor);
begin
  FSelectedItemTextColor := Value;
  Repaint;
end;

function TSRVCustomListBox.GetItem(Position : Integer) : Integer;
var
     i, index : Integer;
begin
  Result := 0;
  index := 0;
  for i:= 0 to Items.Count-1 do begin
    if index = Position then begin
      Result := i;
      Exit;
    end;
    if GetItemHeight(i) > 0 then
      index := index + 1;
  end;
end;

function TSRVCustomListBox.GetPosition(ItemIndex : Integer) : Integer;
var
     i, index : Integer;
begin
  Result := 0;
  index := 0;
  for i:= 0 to Items.Count-1 do begin
    if i = ItemIndex then begin
      Result := index;
      Exit;
    end;
    if GetItemHeight(i) > 0 then
      index := index + 1;
  end;
end;

{ алгоритм быстрой сортировки для символьных строк }
procedure TSRVCustomListBox.QsString(var PItems : array of TSRVLBoxItem; Count : Integer);
var
    PItem : TSRVLBoxItem;

  procedure qs(l, r: integer);
  var
       i, j : integer;
       s : String;
  begin
    i := l;
    j := r;
    s := PItems[(l+r) div 2].Caption;
    repeat
      while PItems[i].Caption < s do
        i := i+1;
      while s < PItems[j].Caption do
        j := j-1;
      if (i <= j) then
      begin
        PItem := PItems[i];
        PItems[i] := PItems[j];
        PItems[j] := PItem;
        i := i+1;
        j := j-1;
      end;
    until i>j;
    if l<j then
      qs(l, j);
    if l<r then
      qs(i, r);
  end;
begin
  if (Count > 1) then
    qs(0, Count-1);
end; { конец быстрой сортировки }

procedure TSRVCustomListBox.SetDoubleBuffered(Value: Boolean);
begin
  if FDoubleBuffered = Value then
    Exit;
  FDoubleBuffered := Value;
  If Value then begin
    Buffer := TBitmap.Create;
    Buffer.Width := Round(Width * ScaleX);
    Buffer.Height := Round(Height * ScaleY);
  end else
    Buffer.Free;
  Repaint;
end;

procedure TSRVCustomListBox.SetSorted(Value: Boolean);
var
   NewItems : TSRVLBoxItemCollection;

  function Sort(Index, NewIndex : Integer) : Integer;
  var
       i, k, j, Indent : integer;
       PItems, PItems2 : array of TSRVLBoxItem;
       HasChild : Boolean;
  begin
    result :=0;
    // выделяем список PItems
    SetLength(PItems, 10);

    PItems[0] := TSRVLBoxItem(Items.Items[Index]);
    Indent := PItems[0].Indent;
    HasChild := srvioCanHaveChildren in PItems[0].Option;
    i := Index+1;
    k := 1;
    while (i < Items.Count) do begin
      if TSRVLBoxItem(Items.Items[i]).Indent < Indent then
        Break;
      if ((TSRVLBoxItem(Items.Items[i]).Indent > Indent) and not HasChild) or
          (TSRVLBoxItem(Items.Items[i]).Indent = Indent) then begin
        if Length(PItems) <= k then
          SetLength(PItems, k * 2);
        PItems[k] := TSRVLBoxItem(Items.Items[i]);
        HasChild := srvioCanHaveChildren in PItems[k].Option;
        k := k + 1;
      end;
      i := i + 1;
    end;

    QsString(PItems, k);
    // Добавляем новые элементы
    SetLength(PItems2, k);
    for j := 0 to k-1 do begin
      PItems2[j] := TSRVLBoxItem(NewItems.Insert(NewIndex + j));
      PItems2[j].Assign(PItems[j]);
    end;

    // ищем детей сортируем их
    for i:=0 to k-1 do begin
      Index := PItems[i].Index + 1;
      if (Index < Items.Count) and
         (srvioCanHaveChildren in PItems[i].Option) and
         (TSRVLBoxItem(Items.Items[Index]).Indent > PItems[i].Indent) then
        Sort(Index, PItems2[i].Index+1);
    end;

    Finalize(PItems);
    Finalize(PItems2);
  end;

begin
  BeginUpdate;
  FSorted := Value;
  NewItems := TSRVLBoxItemCollection.Create(TSRVLBoxItem, Self);
  if (Items.Count > 0) and Value then
    Sort(0, 0);
  FItems.Destroy;
  NewItems.UpdateTree;
  FItems := NewItems;
  FItems.OnAppend := HookOnAppend;
  FItems.OnModify := HookOnModify;
  FItems.OnRemove := HookOnRemove;
  EndUpdate;
end;

procedure TSRVCustomListBox.HookOnScroll(Sender: TObject; ScrollCode: TScrollCode;
  var ScrollPos: Integer);
begin
  FirstItem := Max(GetItem(ScrollPos), 0);
  Repaint;
end;

procedure TSRVCustomListBox.SetScrollBarSkinSchemeIndex(Value : Integer);
begin
  FScrollBarSkinSchemeIndex := Value;
  FScrollBar.SkinSchemeIndex := Value;
  Repaint;
end;

procedure TSRVCustomListBox.SetSkinManager(Value : TSRVSkinManager);
begin
    FScrollBar.SkinManager := Value;
  inherited;
end;

procedure TSRVCustomListBox.WMEraseBkgnd(var Message: TWMPaint);
begin
  Message.Result := 1;
end;

function TSRVCustomListBox.CalculateItemCountToHeight(Height : Integer) : Integer;
var
     i, nH, H : Integer;
begin
  Result := 0;
  nH := FListMarginHeight;
  for i:= 0 to Items.Count-1 do begin
    H := GetItemHeight(i);
    if H > 0 then begin
      nH := nH + H;
      If nH <= Height - ListMarginHeight then
        Result := Result + 1
      else
        Break;
    end;
  end;
end;

procedure TSRVCustomListBox.WMGetDlgCode(var Message: TMessage);
begin
  Message.Result := DLGC_WANTARROWS or DLGC_WANTCHARS;
end;

procedure TSRVCustomListBox.CheckOpenTree(Value: Integer);
var
     i, Indent : Integer;
begin
  if (Value < 0) or (Value >= Items.Count) then Exit;
  // открываем все списки в которые вложен элемент
  Indent := TSRVLBoxItem(Items.Items[Value]).Indent;
  BeginUpdate;
  for i := Value downto 0 do
    if Indent = 0 then break
    else if TSRVLBoxItem(Items.Items[i]).Indent < Indent then begin
      if not TSRVLBoxItem(Items.Items[i]).Expanded then
        TSRVLBoxItem(Items.Items[i]).Expanded := True;
      Indent := TSRVLBoxItem(Items.Items[i]).Indent;
    end;
  EndUpdate;
end;

procedure TSRVCustomListBox.SetHideSelection(Value: Boolean);
begin
  FHideSelection := Value;
  Repaint;
end;

function TSRVCustomListBox.ItemHasChild(Index : Integer) : Boolean;
begin
  Result := False;
  If (Index < 0) or (Index >= Items.Count-1) then
    exit;

  Result := (srvioCanHaveChildren in TSRVLBoxItem(Items.Items[Index]).Option) and
    (TSRVLBoxItem(Items.Items[Index]).Indent < TSRVLBoxItem(Items.Items[Index+1]).Indent);
end;

function TSRVCustomListBox.ItemHasParent(Index : Integer) : Boolean;
var
     Indent : Integer;
begin
  Result := False;
  If (Index < 0) or (Index >= Items.Count) then
    exit;

  Indent := TSRVLBoxItem(Items.Items[Index]).Indent;
  Dec(Index);

  while (Index >= 0) and (TSRVLBoxItem(Items.Items[Index]).Indent = Indent) do begin
    Indent := TSRVLBoxItem(Items.Items[Index]).Indent;
    Dec(Index);
  end;

  Result := (Index >= 0) and
    (TSRVLBoxItem(Items.Items[Index]).Indent > Indent) and
    (srvioCanHaveChildren in TSRVLBoxItem(Items.Items[Index]).Option) and
    (srvioCanSelect in TSRVLBoxItem(Items.Items[Index]).Option);
end;

function TSRVCustomListBox.GetParent(Index : Integer) : Integer;
var
     Indent : Integer;
begin
  Result := -1;
  If (Index < 0) or (Index >= Items.Count) then
    exit;

  Indent := TSRVLBoxItem(Items.Items[Index]).Indent;
  Dec(Index);

  while (Index < 0) and (TSRVLBoxItem(Items.Items[Index]).Indent = Indent) do begin
    Indent := TSRVLBoxItem(Items.Items[Index]).Indent;
    Dec(Index);
  end;

  if (Index >= 0) and
     (TSRVLBoxItem(Items.Items[Index]).Indent > Indent) and
     (srvioCanHaveChildren in TSRVLBoxItem(Items.Items[Index]).Option) and
     (srvioCanSelect in TSRVLBoxItem(Items.Items[Index]).Option) then
    Result := Index;
end;

{-----------------------------------------------------------------------}
procedure TSRVCustomListBox.WMRepaintControl(var Message: TMessage);
begin
  If ParentSRV then begin
    if Parent <> nil then
      PostMessage(Parent.Handle, WM_REPAINT_CONTROL, 0, 0);
  end else
    Repaint;
end;

{-----------------------------------------------------------------------}
procedure TSRVCustomListBox.SetListMarginWidth(Value : Integer);
begin
  FListMarginWidth := Value;
  Reset;
  Repaint;
end;

{-----------------------------------------------------------------------}
procedure TSRVCustomListBox.SetListMarginHeight(Value : Integer);
begin
  FListMarginHeight := Value;
  Reset;
  Repaint;
end;

procedure TSRVCustomListBox.ItemHint(Index : Integer);
var
  NewHint : String;
{$IFDEF RICHVIEWDEF5}
  pt: Tpoint;
{$ENDIF}
begin
  If (Index >= 0) and (Index < Items.Count) then
    NewHint := TSRVLBoxItem(Items.Items[Index]).Hint
  else
    NewHint := '';

  if ShowHint and (Hint <> NewHint) then begin
    Hint := NewHint;

    if Hint = '' then
    begin
{$IFDEF RICHVIEWDEF2009}
      if CustomHint <> nil then
        CustomHint.HideHint;
{$ENDIF}
      Application.CancelHint;
    end else
{$IFDEF RICHVIEWDEF5}
{$IFDEF RICHVIEWDEF2009}
    if CustomHint <> nil then
      CustomHint.ShowHint(Self)
    else
{$ENDIF}
    begin
      GetCursorPos(pt);
      Application.ActivateHint(pt);
    end;
{$ENDIF}
  end;
end;

end.

