home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 September / Chip_2001-09_cd1.bin / zkuste / delphi / kolekce / d56 / RMCTL.ZIP / rmCollectionListBox.pas < prev    next >
Pascal/Delphi Source File  |  2001-06-22  |  24KB  |  902 lines

  1. {================================================================================
  2. Copyright (C) 1997-2001 Mills Enterprise
  3.  
  4. Unit     : rmCollectionListBox
  5. Purpose  : Allow for multi-line/multi-height "listbox" type functionality.  Also
  6.            allowing for Icons to be specified for each item.
  7. Date     : 04-24-2000
  8. Author   : Ryan J. Mills
  9. Version  : 1.80
  10. ================================================================================}
  11.  
  12. unit rmCollectionListBox;
  13.  
  14. interface
  15.  
  16. {$I CompilerDefines.INC}
  17.  
  18. uses Messages, Windows, Forms, Controls, ImgList, Graphics, Classes, sysutils;
  19.  
  20. type
  21.   TrmCollectionListBox = class;
  22.  
  23.   TrmListBoxCollectionItem = class(TCollectionItem)
  24.   private
  25.     FTextData: TStringList;
  26.     FLCount: integer; //Number of lines of text with the current font and display rect;
  27.     fLStart: integer; //Calculated line start of the current record;
  28.     fLRect: TRect; //Calculated Lines display Rect;
  29.     FAlignment: TAlignment;
  30.     fImageIndex: integer;
  31.     fCenterImage: boolean;
  32.     FData: TObject;
  33.     procedure SetAlignment(Value: TAlignment);
  34.     procedure SetTextData(const Value: TStringList);
  35.     procedure SetImageIndex(Value: Integer);
  36.     procedure SetCenterImage(const Value: boolean);
  37.     function GetText: string;
  38.     procedure SetText(const Value: string);
  39.     property Text: string read GetText write SetText;
  40.   public
  41.     constructor Create(Collection: TCollection); override;
  42.     destructor Destroy; override;
  43.     procedure Assign(Source: TPersistent); override;
  44.     property Data: TObject read fData write fData;
  45.     property LCount: integer read fLCount write fLCount;
  46.     property LStart: integer read fLStart write fLStart;
  47.     property LRect: TRect read fLRect write fLRect;
  48.   published
  49.     property Alignment: TAlignment read FAlignment write SetAlignment default taLeftJustify;
  50.     property TextData: TstringList read FTextData write SetTextData;
  51.     property ImageIndex: Integer read fImageIndex write SetImageIndex default -1;
  52.     property CenterImage: boolean read fCenterImage write SetCenterImage default false;
  53.   end;
  54.  
  55.   TrmListBoxCollection = class(TCollection)
  56.   private
  57.     FCollectionListBox: TrmCollectionListBox;
  58.     FOnUpdate: TNotifyEvent;
  59.     function GetItem(Index: Integer): TrmListBoxCollectionItem;
  60.     procedure SetItem(Index: Integer; Value: TrmListBoxCollectionItem);
  61.   protected
  62.     function GetOwner: TPersistent; override;
  63.     procedure Update(Item: TCollectionItem); override;
  64.   public
  65.     constructor Create(CollectionListBox: TrmCollectionListBox);
  66.     function Add: TrmListBoxCollectionItem;
  67.     procedure Delete(Index: Integer);
  68.     function Insert(Index: Integer): TrmListBoxCollectionItem;
  69.     property Items[Index: Integer]: TrmListBoxCollectionItem read GetItem write SetItem; default;
  70.     property OnCollectionUpdate: TNotifyEvent read fOnUpdate write fOnUpdate;
  71.   end;
  72.  
  73.   TrmCollectionListBox = class(TCustomControl)
  74.   private
  75.     fItems: TrmListBoxCollection;
  76.     fSelectedItemIndex: integer;
  77.     fFocusedItemIndex: integer;
  78.     fTopIndex : integer;
  79.     fVScrollSize: longint;
  80.     fVScrollPos: longint;
  81.     fTotalLineCount : integer;
  82.  
  83.     fFocusRect: TRect;
  84.  
  85.     FBorderStyle: TBorderStyle;
  86.     FImageChangeLink: TChangeLink;
  87.     FImages: TCustomImageList;
  88.     fOddColor: TColor;
  89.     fClick: TNotifyEvent;
  90.     fAutoSelect: boolean;
  91.     procedure ImageListChange(Sender: TObject);
  92.  
  93.     procedure SetImages(Value: TCustomImageList);
  94.     procedure SetItems(const Value: TrmListBoxCollection);
  95.     procedure SetOddColor(const Value: TColor);
  96.     procedure SetItemIndex(const Value: integer);
  97.     procedure SetBorderStyle(const Value: TBorderStyle);
  98.  
  99.     procedure cmFOCUSCHANGED(var MSG:TMessage); message CM_FOCUSCHANGED;
  100.  
  101.     procedure CalcVScrollSize(startIndex: integer);
  102.     procedure UpdateVScrollSize;
  103.     function UpdateVScrollPos(newPos: integer): boolean;
  104.  
  105.     procedure WMSize(var Msg: TWMSize); message WM_SIZE;
  106.     procedure WMVScroll(var Msg: TWMVScroll); message WM_VSCROLL;
  107.  
  108.     procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  109.     procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
  110.   protected
  111.     procedure CreateParams(var Params: TCreateParams); override;
  112.     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  113.     procedure Paint; override;
  114.     function VisibleLineCount: integer;
  115.     function LineHeight: integer;
  116.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  117.     procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  118.     function TopItemIndex: integer;
  119.   public
  120.     constructor Create(AOwner: TComponent); override;
  121.     destructor destroy; override;
  122.     procedure loaded; override;
  123.     function Add(aText: string; aImageIndex: integer; aData: TObject): integer;
  124.     function Insert(Index: integer; aText: string; aImageIndex: integer; aData: TObject): integer;
  125.     procedure Delete(Index: integer);
  126.     property ItemIndex: integer read fselectedItemIndex write SetItemIndex;
  127.   published
  128.     property AutoSelect : boolean read fAutoSelect write fAutoSelect default false;
  129.     property Collection: TrmListBoxCollection read fItems write setItems;
  130.     property Images: TCustomImageList read FImages write SetImages;
  131.     property OddColor: TColor read fOddColor write SetOddColor default clInfoBk;
  132.     property Align;
  133.     property Anchors;
  134.     property BorderStyle: TBorderStyle read FBorderStyle write SetBorderStyle default bsSingle;
  135.     property Color default clWindow;
  136.     property Constraints;
  137.     property Ctl3D;
  138.     property Font;
  139.     property ParentColor default False;
  140.     property ParentCtl3D;
  141.     property ParentFont;
  142.     property ParentShowHint;
  143.     property PopupMenu;
  144.     property ShowHint;
  145.     property TabOrder;
  146.     property TabStop default true;
  147.     property Visible;
  148.  
  149.     property OnClick: TNotifyEvent read fClick write fClick;
  150.     property OnContextPopup;
  151.     property OnDblClick;
  152.     property OnDragDrop;
  153.     property OnDragOver;
  154.     property OnEnter;
  155.     property OnExit;
  156.     property OnKeyDown;
  157.     property OnKeyPress;
  158.     property OnKeyUp;
  159.     property OnMouseDown;
  160.     property OnMouseMove;
  161.     property OnMouseUp;
  162.   end;
  163.  
  164. implementation
  165.  
  166. uses rmMsgList, rmLibrary;
  167.  
  168. { TrmListBoxCollectionItem }
  169.  
  170. constructor TrmListBoxCollectionItem.Create(Collection: TCollection);
  171. begin
  172.   fImageIndex := -1;
  173.   FTextData := TStringList.create;
  174.   inherited Create(Collection);
  175. end;
  176.  
  177. procedure TrmListBoxCollectionItem.Assign(Source: TPersistent);
  178. begin
  179.   if Source is TrmListBoxCollectionItem then
  180.   begin
  181.     TextData.Assign(TrmListBoxCollectionItem(Source).TextData);
  182.     Alignment := TrmListBoxCollectionItem(Source).Alignment;
  183.     ImageIndex := TrmListBoxCollectionItem(Source).ImageIndex;
  184.   end
  185.   else
  186.     inherited Assign(Source);
  187. end;
  188.  
  189. procedure TrmListBoxCollectionItem.SetAlignment(Value: TAlignment);
  190. begin
  191.   if FAlignment <> Value then
  192.   begin
  193.     FAlignment := Value;
  194.     Changed(False);
  195.   end;
  196. end;
  197.  
  198. procedure TrmListBoxCollectionItem.SetTextData(const Value: TStringList);
  199. begin
  200.   fTextData.Assign(Value);
  201.   Changed(False);
  202. end;
  203.  
  204. procedure TrmListBoxCollectionItem.SetImageIndex(Value: Integer);
  205. begin
  206.   if FImageIndex <> Value then
  207.   begin
  208.     FImageIndex := Value;
  209.     Changed(False);
  210.   end;
  211. end;
  212.  
  213. procedure TrmListBoxCollectionItem.SetCenterImage(const Value: boolean);
  214. begin
  215.   if fCenterImage <> value then
  216.   begin
  217.     fCenterImage := value;
  218.     changed(false);
  219.   end;
  220. end;
  221.  
  222. function TrmListBoxCollectionItem.GetText: string;
  223. begin
  224.   result := FTextData.Text;
  225. end;
  226.  
  227. destructor TrmListBoxCollectionItem.Destroy;
  228. begin
  229.   FTextData.free;
  230.   inherited;
  231. end;
  232.  
  233. procedure TrmListBoxCollectionItem.SetText(const Value: string);
  234. begin
  235.   FTextData.text := Value;
  236.  
  237. end;
  238.  
  239. { TrmListBoxCollection }
  240.  
  241. constructor TrmListBoxCollection.Create(CollectionListBox: TrmCollectionListBox);
  242. begin
  243.   inherited Create(TrmListBoxCollectionItem);
  244.   FCollectionListBox := CollectionListBox;
  245. end;
  246.  
  247. function TrmListBoxCollection.Add: TrmListBoxCollectionItem;
  248. begin
  249.   Result := TrmListBoxCollectionItem(inherited Add);
  250. end;
  251.  
  252. function TrmListBoxCollection.GetItem(Index: Integer): TrmListBoxCollectionItem;
  253. begin
  254.   Result := TrmListBoxCollectionItem(inherited GetItem(Index));
  255. end;
  256.  
  257. function TrmListBoxCollection.GetOwner: TPersistent;
  258. begin
  259.   Result := FCollectionListBox;
  260. end;
  261.  
  262. procedure TrmListBoxCollection.SetItem(Index: Integer; Value: TrmListBoxCollectionItem);
  263. begin
  264.   inherited SetItem(Index, Value);
  265. end;
  266.  
  267. procedure TrmListBoxCollection.Update(Item: TCollectionItem);
  268. begin
  269.   inherited;
  270.   if assigned(fOnUpdate) then
  271.     fOnUpdate(item);
  272. end;
  273.  
  274. procedure TrmListBoxCollection.Delete(Index: Integer);
  275. begin
  276.   inherited Delete(index);
  277. end;
  278.  
  279. function TrmListBoxCollection.Insert(
  280.   Index: Integer): TrmListBoxCollectionItem;
  281. begin
  282.   Result := TrmListBoxCollectionItem(inherited Insert(index));
  283. end;
  284.  
  285. { TrmCollectionListBox }
  286.  
  287. constructor TrmCollectionListBox.Create(AOwner: TComponent);
  288. begin
  289.   inherited Create(AOwner);
  290.  
  291.   ControlStyle := [csClickEvents, csDoubleClicks, csOpaque];
  292.  
  293.   if not NewStyleControls then
  294.     ControlStyle := ControlStyle + [csFramed];
  295.  
  296.  
  297.   Width := 121;
  298.   Height := 97;
  299.   TabStop := True;
  300.   ParentColor := False;
  301.   fVScrollPos := 0;
  302.   fVScrollSize := 0;
  303.   fselectedItemIndex := -1;
  304.   fTopIndex := 0;
  305.   ffocusedItemIndex := 0;
  306.   fOddColor := clInfoBk;
  307.   Color := clWindow;
  308.   fborderstyle := bsSingle;
  309.   fAutoSelect := false;
  310.  
  311.   fItems := TrmListBoxCollection.create(self);
  312.  
  313.   FImageChangeLink := TChangeLink.Create;
  314.   FImageChangeLink.OnChange := ImageListChange;
  315. end;
  316.  
  317. procedure TrmCollectionListBox.CreateParams(var Params: TCreateParams);
  318. begin
  319.   inherited CreateParams(Params);
  320.   with Params do
  321.   begin
  322.     if TabStop then Style := Style or WS_TABSTOP;
  323.     Style := Style or WS_VSCROLL;
  324.     WindowClass.style := CS_DBLCLKS;
  325.  
  326.     if FBorderStyle = bsSingle then
  327.     begin
  328.       if NewStyleControls and Ctl3D then
  329.       begin
  330.         Style := Style and not WS_BORDER;
  331.         ExStyle := ExStyle or WS_EX_CLIENTEDGE;
  332.       end
  333.       else
  334.         Style := Style or WS_BORDER;
  335.     end;
  336.   end;
  337. end;
  338.  
  339. procedure TrmCollectionListBox.SetImages(Value: TCustomImageList);
  340. begin
  341.   if Images <> nil then
  342.     Images.UnRegisterChanges(FImageChangeLink);
  343.   FImages := Value;
  344.   if Images <> nil then
  345.   begin
  346.     Images.RegisterChanges(FImageChangeLink);
  347.     Images.FreeNotification(Self);
  348.     CalcVScrollSize(-1);
  349.     invalidate;
  350.   end;
  351. end;
  352.  
  353. procedure TrmCollectionListBox.ImageListChange(Sender: TObject);
  354. begin
  355.   Invalidate;
  356. end;
  357.  
  358. procedure TrmCollectionListBox.Notification(AComponent: TComponent;
  359.   Operation: TOperation);
  360. begin
  361.   inherited Notification(AComponent, Operation);
  362.   if (Operation = opRemove) and (AComponent = Images) then
  363.     Images := nil;
  364. end;
  365.  
  366. procedure TrmCollectionListBox.setItems(const Value: TrmListBoxCollection);
  367. begin
  368.   if fitems <> value then
  369.   begin
  370.     fItems.assign(Value);
  371.     invalidate;
  372.   end;
  373. end;
  374.  
  375. destructor TrmCollectionListBox.destroy;
  376. begin
  377.   FImageChangeLink.Free;
  378.   fItems.Free;
  379.   inherited Destroy;
  380. end;
  381.  
  382. procedure TrmCollectionListBox.CalcVScrollSize(startIndex: integer);
  383. var
  384.   loop: integer;
  385.   wCalcSize : integer;
  386.   wImageRect, wTextRect, wCalcRect: TRect;
  387.   wFHeight: integer;
  388.   wStr : string;
  389. begin
  390.   if csloading in componentstate then exit;
  391.   if csDestroying in ComponentState then exit;
  392.  
  393.   if assigned(fImages) then
  394.   begin
  395.     wImageRect := Rect(0, 0, FImages.Width, FImages.Height);
  396.     wTextRect := Rect(fImages.Width + 2, 0, clientwidth - (fImages.Width + 2), FImages.Height);
  397.   end
  398.   else
  399.   begin
  400.     wImageRect := Rect(0, 0, 0, 0);
  401.     wTextRect := Rect(2, 0, clientwidth, 0);
  402.   end;
  403.  
  404.   if startindex = -1 then
  405.   begin
  406.     loop := 0;
  407.     wCalcSize := 0;
  408.   end
  409.   else
  410.   begin
  411.      loop := startindex;
  412.      if startindex >= 1 then
  413.        wcalcSize := fitems.Items[startindex-1].lstart + fitems.Items[startindex-1].lcount
  414.      else
  415.        wCalcSize := 0;
  416.   end;
  417.  
  418.   wFHeight := LineHeight;
  419.  
  420.   while loop < fItems.Count do
  421.   begin
  422.     with fItems.Items[loop] do
  423.     begin
  424.       wCalcRect := wTextRect;
  425.       wstr := trim(Text);
  426.       DrawText(Canvas.Handle, pchar(wStr), length(wStr), wCalcRect, DT_WORDBREAK or DT_CALCRECT);
  427.  
  428.       if (wCalcRect.Bottom - wCalcRect.Top) < (wImageRect.Bottom-wImageRect.Top) then
  429.          wCalcRect.Bottom := wImageRect.Bottom;
  430.  
  431.       LCount := (wCalcRect.Bottom - wCalcRect.Top) div wFHeight;
  432.  
  433.       if LCount = 0 then
  434.         lCount := 1;
  435.  
  436.       if (((wCalcRect.Bottom - wCalcRect.Top) mod wFHeight) > (wFHeight div 2)) then
  437.         lCount := lCount + 1;
  438.  
  439.       LStart := wCalcSize;
  440.       LRect := rect(wCalcRect.left, wCalcRect.Top, clientwidth, wCalcRect.bottom);
  441.       inc(wCalcSize, LCount);
  442.     end;
  443.     inc(loop);
  444.   end;
  445.  
  446.   fTotalLineCount := wCalcSize;
  447.   UpdateVScrollSize;
  448. end;
  449.  
  450. procedure TrmCollectionListBox.loaded;
  451. begin
  452.   inherited;
  453.   CalcVScrollSize(-1);
  454. end;
  455.  
  456. procedure TrmCollectionListBox.WMSize(var Msg: TWMSize);
  457. begin
  458.   inherited;
  459.   CalcVScrollSize(-1);
  460.   UpdateVScrollPos(fVScrollPos + 1);
  461.   UpdateVScrollPos(fVScrollPos - 1);
  462.   Invalidate;
  463. end;
  464.  
  465. procedure TrmCollectionListBox.Paint;
  466. var
  467.   index: integer;
  468.   wImageRect, wCalcRect: TRect;
  469.   wFHeight, cheight, wcalcheight: integer;
  470.   DrawFlags: integer;
  471.   wStr : string;
  472.   wImageAdjust : integer;
  473. begin
  474.   if csDestroying in ComponentState then exit;
  475.  
  476.   wFHeight := LineHeight;
  477.  
  478.   index := TopItemIndex;
  479.   if index = -1 then
  480.   begin
  481.     fFocusRect := rect(0, 0, clientwidth, wFHeight);
  482.     if Focused then
  483.        Canvas.DrawFocusRect(fFocusRect)
  484.     else
  485.     begin
  486.        Canvas.Brush.Color := Color;
  487.        Canvas.FillRect(rect(0, 0, clientwidth, wFHeight));
  488.     end;
  489.     exit;
  490.   end;
  491.  
  492.   if assigned(fImages) then
  493.     wImageRect := Rect(0, 0, FImages.Width, FImages.Height)
  494.   else
  495.     wImageRect := Rect(0, 0, 0, 0);
  496.  
  497.   wimageAdjust := 0;
  498.  
  499.   cheight := VisibleLineCount;
  500.  
  501.   while (cheight > 0) and (index < fItems.count) do
  502.   begin
  503.     with fItems.Items[index] do
  504.     begin
  505.       DrawFlags := DT_WORDBREAK;
  506.  
  507.       if FAlignment = taCenter then
  508.         DrawFlags := DrawFlags or DT_CENTER;
  509.  
  510.       if FAlignment = taRightJustify then
  511.         DrawFlags := DrawFlags or DT_RIGHT;
  512.  
  513.       wCalcRect := LRect;
  514.  
  515.       OffsetRect(wCalcRect, 0, ((LStart - fVScrollPos) * wFHeight)+wImageAdjust);
  516.  
  517.       if assigned(fimages) and ((lcount*wfHeight) < fimages.height) then
  518.          inc(wimageAdjust, fimages.height - (lcount*wfHeight));
  519.  
  520.       if wCalcRect.Top < 0 then
  521.          wcalcheight := (LStart - fVScrollPos) - lCount
  522.       else
  523.          wCalcheight := lcount;
  524.  
  525.       dec(cheight, wcalcheight);
  526.  
  527.       if index = fSelectedItemIndex then
  528.       begin
  529.         if assigned(fimages) then
  530.         begin
  531.           Canvas.Brush.Color := clHighlight;
  532.           Canvas.fillrect(rect(0, wCalcRect.top, wImageRect.right, wCalcRect.Bottom));
  533.  
  534.           if odd(index) then
  535.             canvas.brush.color := color
  536.           else
  537.             canvas.brush.color := oddcolor;
  538.  
  539.           Canvas.fillrect(rect(wImageRect.right, wCalcRect.top, wCalcRect.right, wCalcRect.Bottom));
  540.         end
  541.         else
  542.         begin
  543.           Canvas.Font.Color := clHighLightText;
  544.           Canvas.Brush.Color := clHighlight;
  545.           wCalcRect.Right := ClientWidth;
  546.           Canvas.fillrect(rect(0, wCalcRect.top, wCalcRect.right, wCalcRect.Bottom));
  547.         end;
  548.       end
  549.       else
  550.       begin
  551.         Canvas.Font.Color := Font.Color;
  552.         if odd(index) then
  553.           canvas.brush.color := color
  554.         else
  555.           canvas.brush.color := oddcolor;
  556.  
  557.         wCalcRect.Right := ClientWidth;
  558.         Canvas.fillrect(rect(0, wCalcRect.top, wCalcRect.right, wCalcRect.Bottom));
  559.       end;
  560.  
  561.       if assigned(FImages) then
  562.       begin
  563.         if fCenterImage then
  564.           FImages.Draw(canvas, 0, wCalcRect.Top + (((wCalcRect.Bottom - wCalcRect.Top) div 2) - (fImages.Height div 2)), fImageIndex)
  565.         else
  566.           FImages.Draw(canvas, 0, wCalcRect.Top, fImageIndex);
  567.       end;
  568.  
  569.       wstr := Trim(Text);
  570.       DrawText(Canvas.Handle, pchar(wstr), length(wstr), wCalcRect, DrawFlags);
  571.  
  572.       if (index = fFocusedItemIndex) then
  573.       begin
  574.         fFocusRect := rect(0, wCalcRect.Top, clientwidth, wCalcRect.bottom);
  575.         if focused then
  576.           Canvas.DrawFocusRect(fFocusRect);
  577.       end;
  578.     end;
  579.     inc(index);
  580.   end;
  581.  
  582.   if cHeight > 0 then
  583.   begin
  584.     Canvas.Brush.Color := color;
  585.     Canvas.FillRect(rect(0, wCalcRect.Bottom, clientwidth, wCalcRect.Bottom + ((cHeight + 1) * wFHeight)));
  586.   end;
  587. end;
  588.  
  589. procedure TrmCollectionListBox.UpdateVScrollSize;
  590. var
  591.   wScrollInfo: TScrollInfo;
  592. begin
  593.   fVScrollSize := fTotalLineCount - VisibleLineCount;
  594.   with wScrollInfo do
  595.   begin
  596.     cbSize := sizeof(TScrollInfo);
  597.     fMask := SIF_RANGE or SIF_DISABLENOSCROLL;
  598.     nMin := 0;
  599.     nMax := fVScrollSize;
  600.   end;
  601.  
  602.   SetScrollInfo(Handle, SB_VERT, wScrollInfo, True);
  603. end;
  604.  
  605. procedure TrmCollectionListBox.WMVScroll(var Msg: TWMVScroll);
  606. var
  607.   newPos: integer;
  608. begin
  609.   inherited;
  610.   case Msg.ScrollCode of
  611.     SB_BOTTOM: newPos := fItems.Items[fItems.Count - 1].LStart;
  612.     SB_LINEDOWN: newPos := fVScrollPos + 1;
  613.     SB_LINEUP: newPos := fVScrollPos - 1;
  614.     SB_TOP: newPos := 0;
  615.     SB_PAGEDOWN: newPos := fVScrollPos + VisibleLineCount;
  616.     SB_PAGEUP: newPos := fVScrollPos - VisibleLineCount;
  617.     SB_THUMBPOSITION: newPos := Msg.Pos;
  618.     SB_THUMBTRACK: newPos := msg.Pos;
  619.   else
  620.     exit;
  621.   end;
  622.  
  623.   if UpdateVScrollPos(newPos) then
  624.     Invalidate;
  625. end;
  626.  
  627. function TrmCollectionListBox.UpdateVScrollPos(newPos: integer): Boolean;
  628. var
  629.   wScrollInfo: TScrollInfo;
  630. begin
  631.   result := false;
  632.  
  633.   if (newPos <= 0) and (fVScrollPos = 0) then
  634.     exit;
  635.  
  636.   if (newPos > fVScrollSize) and (fVScrollPos = fVScrollSize) then
  637.     exit;
  638.  
  639.   if (newPos = fVscrollPos) then
  640.     exit;
  641.  
  642.   result := true;
  643.  
  644.   if newpos < 0 then
  645.     fVScrollPos := 0
  646.   else if newpos > fVscrollSize then
  647.     fVScrollPos := fVScrollSize
  648.   else
  649.     fVScrollPos := newPos;
  650.  
  651.   if fVScrollPos < 0 then
  652.      fVScrollPos := 0;
  653.  
  654.   with wScrollInfo do
  655.   begin
  656.     cbSize := sizeof(TScrollInfo);
  657.     fMask := SIF_POS;
  658.     nPos := fVScrollPos;
  659.   end;
  660.   SetScrollInfo(Handle, SB_VERT, wScrollInfo, True);
  661. end;
  662.  
  663. function TrmCollectionListBox.VisibleLineCount: integer;
  664. begin
  665.   result := (clientheight div LineHeight);
  666. end;
  667.  
  668. function TrmCollectionListBox.LineHeight: integer;
  669. var
  670.   TM: tagTextMetricA;
  671. begin
  672.   GetTextMetrics(Canvas.Handle, TM);
  673.   result := TM.tmHeight;
  674. end;
  675.  
  676. function TrmCollectionListBox.Add(aText: string; aImageIndex: integer; aData: TObject): integer;
  677. begin
  678.   with fItems.Add do
  679.   begin
  680.     TextData.text := aText;
  681.     ImageIndex := aImageIndex;
  682.     Data := aData;
  683.     result := ItemIndex;
  684.   end;
  685.   CalcVScrollSize(fItems.count-1);
  686.   UpdateVScrollSize;
  687.   invalidate;
  688. end;
  689.  
  690. procedure TrmCollectionListBox.Delete(Index: integer);
  691. begin
  692.   fItems.delete(index);
  693.   CalcVScrollSize(index-1);
  694.   UpdateVScrollSize;
  695.   invalidate;
  696. end;
  697.  
  698. function TrmCollectionListBox.Insert(Index: integer; aText: string; aImageIndex: integer;
  699.   aData: TObject): integer;
  700. begin
  701.   with fItems.Insert(Index) do
  702.   begin
  703.     TextData.text := aText;
  704.     ImageIndex := aImageIndex;
  705.     Data := aData;
  706.     result := ItemIndex;
  707.   end;
  708.   CalcVScrollSize(-1);
  709.   UpdateVScrollSize;
  710.   invalidate;
  711. end;
  712.  
  713. procedure TrmCollectionListBox.SetOddColor(const Value: TColor);
  714. begin
  715.   if fOddColor <> Value then
  716.   begin
  717.     fOddColor := Value;
  718.     invalidate;
  719.   end;
  720. end;
  721.  
  722. procedure TrmCollectionListBox.CMFontChanged(var Message: TMessage);
  723. begin
  724.   inherited;
  725.   Canvas.Font.assign(font);
  726.   CalcVScrollSize(-1);
  727.   invalidate;
  728. end;
  729.  
  730. procedure TrmCollectionListBox.MouseDown(Button: TMouseButton;
  731.   Shift: TShiftState; X, Y: Integer);
  732. var
  733.   index: integer;
  734.   found: boolean;
  735.   wTextRect, wCalcRect: TRect;
  736.   wFHeight, cheight: integer;
  737.   DrawFlags: integer;
  738.   wStr : String;
  739. begin
  740.   inherited;
  741.  
  742.   if Button = mbLeft then
  743.   begin
  744.     if CanFocus then
  745.        setfocus;
  746.     try
  747.       index := TopItemIndex;
  748.       if index <> -1 then
  749.       begin
  750.         wFHeight := LineHeight;
  751.         wTextRect := Rect(0, 0, clientwidth, 0);
  752.         cheight := VisibleLineCount;
  753.  
  754.         found := false;
  755.         while (cheight > 0) and (index < fItems.count) and not found do
  756.         begin
  757.           with fItems.Items[index] do
  758.           begin
  759.             DrawFlags := DT_WORDBREAK;
  760.  
  761.             if FAlignment = taCenter then
  762.               DrawFlags := DrawFlags or DT_CENTER;
  763.  
  764.             if FAlignment = taRightJustify then
  765.               DrawFlags := DrawFlags or DT_RIGHT;
  766.  
  767.             wCalcRect := wTextRect;
  768.             wStr := trim(Text);
  769.             DrawText(Canvas.Handle, pchar(wstr), length(wstr), wCalcRect, DrawFlags or DT_CALCRECT);
  770.             wCalcRect.Right := clientwidth;
  771.  
  772.             OffsetRect(wCalcRect, 0, (LStart - fVScrollPos) * wFHeight);
  773.  
  774.             found := ptinrect(wCalcRect, Point(x, y));
  775.  
  776.             if found then
  777.               break;
  778.  
  779.             dec(cheight, (LStart - fVScrollPos) - lCount);
  780.           end;
  781.           inc(index);
  782.         end;
  783.  
  784.         if found then
  785.         begin
  786.           ItemIndex := index;
  787.           fFocusedItemIndex := index;
  788.         end
  789.         else
  790.           ItemIndex := -1;
  791.       end
  792.       else
  793.         ItemIndex := -1;
  794.     finally
  795.       if assigned(fClick) then
  796.         fClick(self);
  797.     end;
  798.   end;
  799. end;
  800.  
  801. procedure TrmCollectionListBox.SetItemIndex(const Value: integer);
  802. begin
  803.   if fSelectedItemIndex <> value then
  804.   begin
  805.     fSelectedItemIndex := Value;
  806.     invalidate;
  807.   end;
  808. end;
  809.  
  810. procedure TrmCollectionListBox.SetBorderStyle(const Value: TBorderStyle);
  811. begin
  812.   if FBorderStyle <> Value then
  813.   begin
  814.     FBorderStyle := Value;
  815.     RecreateWnd;
  816.   end;
  817. end;
  818.  
  819. procedure TrmCollectionListBox.WMGetDlgCode(var Message: TWMGetDlgCode);
  820. begin
  821.   Message.Result := DLGC_WANTARROWS;
  822. end;
  823.  
  824. procedure TrmCollectionListBox.KeyDown(var Key: Word; Shift: TShiftState);
  825. begin
  826.   inherited;
  827.   if fItems.Count > 0 then
  828.   begin
  829.      case key of
  830.        vk_end:  fFocusedItemIndex := fItems.count-1;
  831.        vk_DOWN: inc(fFocusedItemIndex);
  832.        vk_up:   dec(fFocusedItemIndex);
  833.        vk_home: fFocusedItemIndex := 0;
  834.        vk_space, vk_Return:
  835.          begin
  836.            fSelectedItemIndex := fFocusedItemIndex;
  837.            invalidate;
  838.            exit;
  839.          end;
  840.      else
  841.        exit;
  842.      end;
  843.  
  844.      if fFocusedItemIndex < 0 then
  845.         fFocusedItemIndex := 0;
  846.  
  847.      if fFocusedItemIndex >= fItems.count then
  848.         fFocusedItemIndex := fItems.Count-1;
  849.  
  850.      UpdateVScrollPos(fItems[fFocusedItemIndex].fLStart);
  851.  
  852.      if fAutoSelect then
  853.         fSelectedItemIndex := fFocusedItemIndex;
  854.         
  855.      Invalidate;
  856.   end;
  857. end;
  858.  
  859. function TrmCollectionListBox.TopItemIndex: integer;
  860. {var
  861.   index: integer;
  862.   found: boolean;}
  863. begin
  864. {  index := 0;
  865.   found := false;
  866.  
  867.   if fVScrollSize > -1 then
  868.   begin
  869.      while (not found) and (index < fItems.Count) do
  870.      begin
  871.        found := (fVScrollPos >= fItems.Items[index].LStart) and (fVScrollPos <= (fItems.Items[index].lStart + fItems.Items[index].LCount));
  872.        if not found then
  873.          inc(index);
  874.      end;
  875.   end
  876.   else
  877.   begin
  878.      if fItems.count > 0 then
  879.         index := 0
  880.      else
  881.         index := -1;
  882.  
  883.      found := true;
  884.   end;
  885.  
  886.   if not found then
  887.     result := -1
  888.   else
  889.     result := index;}
  890.  
  891.   result := fTopIndex;
  892. end;
  893.  
  894. procedure TrmCollectionListBox.cmFOCUSCHANGED(var MSG: TMessage);
  895. begin
  896.    inherited;
  897.    invalidate;  
  898. end;
  899.  
  900. end.
  901.  
  902.