home *** CD-ROM | disk | FTP | other *** search
/ CD Shareware Magazine 1996 December / CD_shareware_12-96.iso / WIN / Programa / ICONCTLS.ZIP / ICONCTLS.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-07-25  |  29.7 KB  |  729 lines

  1. { -------------------------------------------------------------------------------------}
  2. { A Caching Icon ComboBox and ListBox component for Delphi.                            }
  3. { Copyright 1995, Brad Stowers.  All Rights Reserved.                                  }
  4. { This component can be freely used and distributed in commercial and private          }
  5. { environments, provied this notice is not modified in any way.                        }
  6. { -------------------------------------------------------------------------------------}
  7. { Feel free to contact me if you have any questions, comments or suggestions at        }
  8. { bstowers@pobox.com or 72733,3374 on CompuServe.                                      } 
  9. { -------------------------------------------------------------------------------------}
  10. { Date last modified:  07/25/96                                                        }
  11. { -------------------------------------------------------------------------------------}
  12.  
  13. { -------------------------------------------------------------------------------------}
  14. { TIconComboBox v1.03                                                                  }
  15. { -------------------------------------------------------------------------------------}
  16. { Description:                                                                         }
  17. {   A dropdown list style combobox that displays the icons that exist in a given file. }
  18. { Features:                                                                            }
  19. {   Optionally, the control can disable itself when the filename is invalid.           }
  20. {   Optionally, the control can load icons "on demand."  This speeds up the            }
  21. {     initialization process greatly because all icons do not have to be loaded when   }
  22. {     the control is created.                                                          }
  23. {   Many file formats can be read from, including:                                     }
  24. {     .EXE, .DLL, .ICO                                                                 }
  25. {     .ICL { PCTools? Icon Library                                                     }
  26. {     .NIL { Norton Icon Library                                                       }
  27. { -------------------------------------------------------------------------------------}
  28. { A note about TCustomComboBox (parent of TComboBox, and all combo descendants) and    }
  29. { the owner drawn styles (csOwnerDrawFixed and csOwnerDrawVariable):                   }
  30. { TCustomComboBox has a design problem, in my opinion.  The DropDownCount property is  }
  31. { used to specify how many items are to be displayed when the ComboBox is "opened."    }
  32. { This property works fine, as long as the size of what you are displaying is based on }
  33. { the font assigned to the control.  If you look at the TCustomComboBox.AdjustDropDown }
  34. { method in the VCL source code (STDCTRLS.PAS), you will see that to calculate the     }
  35. { size of the dropdown window, the DropDownCount property is multiplied by the height  }
  36. { of the control's font.  This is all well and good if what you are drawing is based   }
  37. { on the font.  In this case, however, we are drawing based on the size of an icon.    }
  38. { The calculation should be based on DropDownCount and the ItemHeight property, IMHO.  }
  39. { There are two ways that I can think of to work around this problem.                  }
  40. {   1) The simplest method is to just ensure that your Font.Height is equal to your    }
  41. { ItemHeight.  Note that you should probably use a TrueType font for Font.Name so that }
  42. { you can cover a wide range of sizes.                                                 }
  43. {   2) Fix the VCL source code so that it does the calculation properly.  I am still   }
  44. { on back-order for my VCL source, so I haven't been able to do this.  I found the     }
  45. { problem looking at the pre-release source code.                                      }
  46. { -------------------------------------------------------------------------------------}
  47. { Revision History:                                                                    }
  48. { 1.00:  + Initial release                                                             }
  49. { 1.01:  + Added read-only property: NumberOfIcons                                     }
  50. {        + Updated demo program to use NumberOfIcons and allow DropDownCount to be     }
  51. {          changed on the fly.                                                         }
  52. {        + Added OnFileChange event.  Useful for updating statics like Number of icons.}
  53. { 1.02:  + Fixed problem under Delphi 2.0.                                             }
  54. { 1.03:  + Resource for 16 and 32-bit version.  This takes the place of a              }
  55. {          DCR file, which can't be compatible with both.  See the $R directive below. }
  56. {          To compile the resource file, use the following                             }
  57. {            Delphi 1: BRCC.EXE -foIconCtls.r16 -31 IconCtls.rc                        }
  58. {            Delphi 2: BRCC32.EXE -foIconCtls.r32 -w32 IconCtls.rc                     }
  59. { -------------------------------------------------------------------------------------}
  60.  
  61. { -------------------------------------------------------------------------------------}
  62. { TIconListBox v1.03                                                                   }
  63. { -------------------------------------------------------------------------------------}
  64. { Description:                                                                         }
  65. {   A listbox that displays the icons that exist in a given file, either horizontally  }
  66. {   or vertically.                                                                     }
  67. { Features:                                                                            }
  68. {   Optionally, the control can disable itself when the filename is invalid.           }
  69. {   Optionally, the control can load icons "on demand."  This speeds up the            }
  70. {     initialization process greatly because all icons do not have to be loaded when   }
  71. {     the control is created.                                                          }
  72. {   Many file formats can be read from, including:                                     }
  73. {     .EXE, .DLL, .ICO                                                                 }
  74. {     .ICL { PCTools? Icon Library                                                     }
  75. {     .NIL { Norton Icon Library                                                       }
  76. {   ListBox can simulate a grid of icons, allowing you to set the number of icons to   }
  77. {     be displayed in both the X and Y direction.  This setting can be changed         }
  78. {     dynamically.                                                                     }
  79. { -------------------------------------------------------------------------------------}
  80. { Revision History:                                                                    }
  81. { 1.00:  + Initial release                                                             }
  82. {        + Updated demo program to use TIconListBox                                    }
  83. { 1.01:  + Added OnFileChange event.  Useful for updating statics like Number of icons.}
  84. {        + Fixed bug in ResetSize that caused width calculation to be incorrect.       }
  85. { 1.02:  + Published Align property.                                                   }
  86. {        + Fixed problem under Delphi 2.0.                                             }
  87. { 1.03:  + Resource for 16 and 32-bit version.  This takes the place of a              }
  88. {          DCR file, which can't be compatible with both.  See the $R directive below. }
  89. {          To compile the resource file, use the following                             }
  90. {            Delphi 1: BRCC.EXE -foIconCtls.r16 -31 IconCtls.rc                        }
  91. {            Delphi 2: BRCC32.EXE -foIconCtls.r32 -w32 IconCtls.rc                     }
  92. { -------------------------------------------------------------------------------------}
  93.  
  94. unit IconCtls;
  95.  
  96. interface
  97.  
  98. {$IFDEF WIN32}
  99.   {$R IconCtls.r32}
  100. {$ELSE}
  101.   {$R IconCtls.r16}
  102. {$ENDIF}
  103.  
  104. uses
  105.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  106.   Forms, Dialogs, StdCtrls, Menus;
  107.  
  108. type
  109.   TIconComboBox = class(TCustomComboBox)
  110.   private
  111.     { Variables for properties }
  112.     FFileName: String;
  113.     FAutoDisable: boolean;
  114.     FEnableCaching: boolean;
  115.     FNumberOfIcons: integer;
  116.     FOnFileChange: TNotifyEvent;
  117.  
  118.     { Routines that should only be used internally by component }
  119.     procedure LoadIcons;
  120.     procedure FreeIcons;
  121.     procedure UpdateEnabledState;
  122.   protected
  123.     { Routines for setting property values and updating affected items }
  124.     procedure SetFileName(Value: String);
  125.     procedure SetAutoDisable(Value: boolean);
  126.     procedure SetEnableCaching(Value: boolean);
  127.  
  128.     { Icon service routines }
  129.     function  ReadIcon(const Index: integer): TIcon;
  130.     function  GetIcon(const Index: integer): TIcon;
  131.  
  132.     { Owner drawing routines }
  133.     procedure MeasureItem(Index: Integer; var Height: Integer);              override;
  134.     procedure DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState); override;
  135.   public
  136.     constructor Create(AOwner: TComponent); override;
  137.   published
  138.     { Name of icon file to display }
  139.     property FileName: string read FFileName write SetFileName;
  140.     { If true, the combobox will be disabled when FileName does not exist }
  141.     property AutoDisable: boolean read FAutoDisable write SetAutoDisable default TRUE;
  142.     { If true, icons will be loaded as needed, instead of all at once }
  143.     property EnableCaching: boolean read FEnableCaching write SetEnableCaching default TRUE;
  144.     { The number of icons in the file.  -1 if FileName is not valid.  }
  145.     property NumberOfIcons: integer read FNumberOfIcons default -1;
  146.     { Useful if you have statics the reflect the number of icons, etc. }
  147.     property OnFileChange: TNotifyEvent read FOnFileChange write FOnFileChange;
  148.  
  149.     { Protected properties in parent that we will make available to everyone }
  150.     property Color;
  151.     property Ctl3D;
  152.     property DragMode;
  153.     property DragCursor;
  154.     property DropDownCount default 5;
  155.     property Enabled;
  156.     property ItemIndex;
  157.     property ParentColor;
  158.     property ParentCtl3D;
  159.     property ParentFont;
  160.     property ParentShowHint;
  161.     property PopupMenu;
  162.     property ShowHint;
  163.     property TabOrder;
  164.     property TabStop;
  165.     property Visible;
  166. {    property OnChange: TNotifyEvent read FOnChange write FOnChange;}
  167.     property OnClick;
  168.     property OnDblClick;
  169.     property OnDragDrop;
  170.     property OnDragOver;
  171.     property OnDropDown;
  172.     property OnEndDrag;
  173.     property OnEnter;
  174.     property OnExit;
  175.     property OnKeyDown;
  176.     property OnKeyPress;
  177.     property OnKeyUp;
  178.   end;
  179.  
  180.   TOrientation = (lbHorizontal, lbVertical);
  181.  
  182.   TIconListBox = class(TCustomListBox)
  183.   private
  184.     { Private declarations }
  185.     FFileName: String;
  186.     FAutoDisable: boolean;
  187.     FEnableCaching: boolean;
  188.     FNumberOfIcons: integer;
  189.     FItemWidth: integer;
  190.     FXIcons: integer;
  191.     FYIcons: integer;
  192.     FOnChange: TNotifyEvent; { Borland forgot this one in the parent, no idea why. }
  193.     FOnFileChange: TNotifyEvent;
  194.  
  195.     { Routines that should only be used internally by component }
  196.     procedure LoadIcons;
  197.     procedure FreeIcons;
  198.     procedure UpdateEnabledState;
  199.     procedure ResetSize;
  200.   protected
  201. {    procedure CreateParams(var Params: TCreateParams);                       override;}
  202.     { Routines for setting property values and updating affected items }
  203.     procedure SetFileName(Value: String);
  204.     procedure SetAutoDisable(Value: boolean);
  205.     procedure SetEnableCaching(Value: boolean);
  206.     procedure SetXIcons(Value: integer);
  207.     procedure SetYIcons(Value: integer);
  208.  
  209.     { Icon service routines }
  210.     function  ReadIcon(const Index: integer): TIcon;
  211.     function  GetIcon(const Index: integer): TIcon;
  212.  
  213.     { Owner drawing routines }
  214.     procedure MeasureItem(Index: Integer; var Height: Integer);              override;
  215.     procedure DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState); override;
  216.   public
  217.     constructor Create(AOwner: TComponent); override;
  218.   published
  219.     { Name of icon file to display }
  220.     property FileName: string read FFileName write SetFileName;
  221.     { If true, the combobox will be disabled when FileName does not exist }
  222.     property AutoDisable: boolean read FAutoDisable write SetAutoDisable default TRUE;
  223.     { If true, icons will be loaded as needed, instead of all at once }
  224.     property EnableCaching: boolean read FEnableCaching write SetEnableCaching default TRUE;
  225.     { The number of icons in the file.  -1 if FileName is not valid.  }
  226.     property NumberOfIcons: integer read FNumberOfIcons default -1;
  227.     { Number of icons that are to be displayed in the listbox.  The width is modified  }
  228.     { automatically when you change this property.                                     }
  229.     property XIcons: integer read FXIcons write SetXIcons default 4;
  230.     { Number of icons that are to be displayed in the listbox.  The height is modified }
  231.     { automatically when you change this property.                                     }
  232.     property YIcons: integer read FYIcons write SetYIcons default 1;
  233.     { Useful if you have statics the reflect the number of icons, etc. }
  234.     property OnFileChange: TNotifyEvent read FOnFileChange write FOnFileChange;
  235.  
  236.     { Protected properties in parent that we will make available to everyone }
  237.     property Align;
  238.     property Color;
  239.     property Ctl3D;
  240.     property DragMode;
  241.     property DragCursor;
  242.     property Enabled;
  243.     property ItemIndex;
  244.     property ParentColor;
  245.     property ParentCtl3D;
  246.     property ParentFont;
  247.     property ParentShowHint;
  248.     property PopupMenu;
  249.     property ShowHint;
  250.     property TabOrder;
  251.     property TabStop;
  252.     property Visible;
  253.     property OnChange: TNotifyEvent read FOnChange write FOnChange;
  254.     property OnClick;
  255.     property OnDblClick;
  256.     property OnDragDrop;
  257.     property OnDragOver;
  258.     property OnEndDrag;
  259.     property OnEnter;
  260.     property OnExit;
  261.     property OnKeyDown;
  262.     property OnKeyPress;
  263.     property OnKeyUp;
  264.   end;
  265.  
  266. procedure Register;
  267.  
  268. implementation
  269.  
  270. uses ShellAPI;
  271.  
  272. { TIconComboBox Component }
  273. constructor TIconComboBox.Create(AOwner: TComponent);
  274. begin
  275.   inherited Create(AOwner);
  276.   { Set default values }
  277.   FileName := '';
  278.   AutoDisable := TRUE;
  279.   EnableCaching := TRUE;
  280.   FNumberOfIcons := -1;
  281.   DropDownCount := 5;
  282.   Style := csOwnerDrawFixed;
  283.   ItemHeight := GetSystemMetrics(SM_CYICON) + 6;
  284.   Height := ItemHeight;
  285.   Font.Name := 'Arial';
  286.   Font.Height := ItemHeight;
  287.   Width := GetSystemMetrics(SM_CXICON) + GetSystemMetrics(SM_CXVSCROLL) + 10;
  288. end;
  289.  
  290. { Initialize the icon handles, which are stored in the Objects property }
  291. procedure TIconComboBox.LoadIcons;
  292. var
  293.   x: integer;
  294.   Icon: TIcon;
  295.   Buff: array[0..255] of char;
  296.   OldCursor: TCursor;
  297. begin
  298.   { Clear any old icon handles }
  299.   FreeIcons;
  300.   { Reset the contents of the combobox }
  301.   Clear;
  302.   { Update the enabled state of the control }
  303.   UpdateEnabledState;
  304.   { If we have a valid file then setup the combobox. }
  305.   if FileExists(FileName) then begin
  306.     { If we are not loading on demand, set the cursor to an hourglass }
  307.     OldCursor := Screen.Cursor;
  308.     if not EnableCaching then
  309.       Screen.Cursor := crHourGlass;
  310.     { Find out how many icons are in the file }
  311.     {$IFDEF WIN32}
  312.       FNumberOfIcons := ExtractIcon(hInstance, StrPCopy(Buff, FileName), -1);
  313.     {$ELSE}
  314.       FNumberOfIcons := ExtractIcon(hInstance, StrPCopy(Buff, FileName), word(-1));
  315.     {$ENDIF}
  316.     { Loop for every icon in the file }
  317.     for x := 0 to NumberOfIcons - 1 do begin
  318.       { If we are not loading on demand... }
  319.       if not EnableCaching then begin
  320.         { Create a TIcon object... }
  321.         Icon := TIcon.Create;
  322.         { and assign the icon to it. }
  323.         Icon.Handle := ExtractIcon(hInstance, Buff, x);
  324.         { Add the icon and a dummy string to the combobox }
  325.         Items.AddObject(Format('%d',[x]), Icon);
  326.       end else
  327.         { We're loading on demand, so just add a dummy string }
  328.         Items.AddObject(Format('%d',[x]), NIL);
  329.     end;
  330.     { Reset the index to the first item. }
  331.     ItemIndex := 0;
  332.     { if not loading on demand, restore the cursor }
  333.     if not EnableCaching then
  334.       Screen.Cursor := OldCursor;
  335.   end;
  336. end;
  337.  
  338. { Free the icon resources we created. }
  339. procedure TIconComboBox.FreeIcons;
  340. var
  341.   x: integer;
  342.   Icon: TIcon;
  343. begin
  344.   { Loop for every icon }
  345.   for x := 0 to Items.Count-1 do begin
  346.     { Get the icon object }
  347.     Icon := TIcon(Items.Objects[x]);  { Don't use GetIcon here! }
  348.     { Free it.  If it is NIL, Free ignores it, so it is safe }
  349.     Icon.Free;
  350.     { Zero out the TIcon we just freed }
  351.     Items.Objects[x] := NIL;
  352.   end;
  353.   { Reset the number of Icons to reflect that we have no file. }
  354.   FNumberOfIcons := -1;
  355. end;
  356.  
  357. { Disable the control if we don't have a valid filename, and option is enabled }
  358. procedure TIconComboBox.UpdateEnabledState;
  359. begin
  360.   if AutoDisable then
  361.     Enabled := FileExists(FileName)
  362.   else
  363.     Enabled := TRUE;
  364.   { This could be compressed into one statement, but I don't think it }
  365.   { is nearly as readable/understandable this way.  Looks like C.     }
  366. { Enabled := (AutoDisable and FileExists(FileName)) or (not AutoDisable); }
  367. end;
  368.  
  369. { Update the filename of the icon file. }
  370. procedure TIconComboBox.SetFileName(Value: String);
  371. begin
  372.   { If new value is same as old, don't reload icons.  That's silly. }
  373.   if FFileName = Value then exit;
  374.   FFileName := Value;
  375.   { Initialize icon handles from new icon file. }
  376.   LoadIcons;
  377.   { Call user event handler, if one exists }
  378.   if assigned(FOnFileChange) then
  379.     FOnFileChange(Self);
  380. end;
  381.  
  382. { Update the AutoDisable property }
  383. procedure TIconComboBox.SetAutoDisable(Value: boolean);
  384. begin
  385.   { If it's the same, we don't need to do anything }
  386.   if Value = FAutoDisable then exit;
  387.   FAutoDisable := Value;
  388.   { Update the enabled state of control based on new AutoDisable setting }
  389.   UpdateEnabledState;
  390. end;
  391.  
  392. { Update the EnableCaching property }
  393. procedure TIconComboBox.SetEnableCaching(Value: boolean);
  394. begin
  395.   { If it's the same, we don't need to do anything }
  396.   if Value = FEnableCaching then exit;
  397.   FEnableCaching := Value;
  398.   { If load on demand is not enabled, we need to load all the icons. }
  399.   if not FEnableCaching then
  400.     LoadIcons;
  401. end;
  402.  
  403. { Used to extract icons from files and assign them to a TIcon object }
  404. function TIconComboBox.ReadIcon(const Index: integer): TIcon;
  405. var
  406.   Buff: array[0..255] of char;
  407. begin
  408.   { Create the new icon }
  409.   Result := TIcon.Create;
  410.   { Assign it the icon handle }
  411.   Result.Handle := ExtractIcon(hInstance, StrPCopy(Buff, FileName), Index);
  412. end;
  413.  
  414. { Returns the icon for a given combobox index }
  415. function TIconComboBox.GetIcon(const Index: integer): TIcon;
  416. begin
  417.   { If load on demand is enabled... }
  418.   if EnableCaching then
  419.     { Has the icon been loaded yet? }
  420.     if Items.Objects[Index] = NIL then
  421.       { No, we must get the icon and add it to Objects }
  422.       Items.Objects[Index] := ReadIcon(Index);
  423.   { Return the requested icon }
  424.   Result := TIcon(Items.Objects[Index]);
  425. end;
  426.  
  427. { Return the size of the item we are drawing }
  428. procedure TIconComboBox.MeasureItem(Index: Integer; var Height: Integer);
  429. begin
  430.   { Ask Windows how tall icons are }
  431.   Height := GetSystemMetrics(SM_CYICON);
  432. end;
  433.  
  434. { Draw the item requested in the given rectangle.  Because of the parent's default }
  435. { behavior, we needn't worry about the State.  That's very nice.                   }
  436. procedure TIconComboBox.DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState);
  437. var
  438.   Icon: TIcon;
  439. begin
  440.   { Use the controls canvas for drawing... }
  441.   with Canvas do begin
  442.     try
  443.       { Fill in the rectangle.  The proper brush has already been set up for us,   }
  444.       { so we needn't use State to set it ourselves.                               }
  445.       FillRect(Rect);
  446.       { Get the icon to be drawn }
  447.       Icon := GetIcon(Index);
  448.       { If nothing has gone wrong, draw the icon.  Theoretically, it should never  }
  449.       { be NIL, but why take the chance?                                           }
  450.       if Icon <> nil then
  451.         { Using the given rectangle, draw the icon on the control's canvas,        }
  452.         { centering it within the rectangle.                                       }
  453.         with Rect do Draw(Left + (Right - Left - Icon.Width) div 2,
  454.                           Top + (Bottom - Top - Icon.Width) div 2, Icon);
  455.     except
  456.       { If anything went wrong, we fall down to here.  You may want to add some    }
  457.       { sort of user notification.  No clean up is necessary since we did not      }
  458.       { create anything.  We'll just ignore the problem and hope it goes away. :)  }
  459.       {!};
  460.     end;
  461.   end;
  462. end;
  463.  
  464.  
  465.  
  466. { TIconListBox Component }
  467.  
  468. constructor TIconListBox.Create(AOwner: TComponent);
  469. begin
  470.   inherited Create(AOwner);
  471.   { Set default values }
  472.   Style := lbOwnerDrawFixed;
  473.   ItemHeight := GetSystemMetrics(SM_CYICON) + 6;
  474.   FItemWidth := GetSystemMetrics(SM_CXICON) + 6;
  475.   Font.Name := 'Arial';
  476.   Font.Height := ItemHeight;
  477.   FileName := '';
  478.   FAutoDisable := TRUE;
  479.   FEnableCaching := TRUE;
  480.   FNumberOfIcons := -1;
  481.   FYIcons := 1;
  482.   { By setting XIcons instead of FXIcons, the windows will get sized }
  483.   XIcons := 4;
  484. end;
  485.  
  486. (*procedure TIconListBox.CreateParams(var Params: TCreateParams);
  487. begin
  488.   inherited CreateParams(Params);
  489. {  if Orientation = lbVertical then
  490.     Params.Style := Params.Style or LBS_DISABLENOSCROLL or WS_VSCROLL and (not WS_HSCROLL)
  491.   else
  492.     Params.Style := Params.Style or LBS_DISABLENOSCROLL or WS_HSCROLL and (not WS_VSCROLL);}
  493. end;*)
  494.  
  495. { Initialize the icon handles, which are stored in the Objects property }
  496. procedure TIconListBox.LoadIcons;
  497.   function CountIcons(Inst: THandle; Filename: PChar): integer;
  498.   var
  499.     TmpIcon: HICON;
  500.   begin
  501.     Result := 0;
  502.     TmpIcon := ExtractIcon(Inst, Filename, Result);
  503.     while (TmpIcon <> 0) do begin
  504.       inc(Result);
  505.       DestroyIcon(TmpIcon);
  506.       TmpIcon := ExtractIcon(Inst, Filename, Result);
  507.     end;
  508.   end;
  509. var
  510.   x: integer;
  511.   Icon: TIcon;
  512.   Buff: array[0..255] of char;
  513.   OldCursor: TCursor;
  514. begin
  515.   { Clear any old icon handles }
  516.   FreeIcons;
  517.   { Reset the contents of the listbox }
  518.   Clear;
  519.   { Update the enabled state of the control }
  520.   UpdateEnabledState;
  521.   { If we have a valid file then setup the combobox. }
  522.   if FileExists(FileName) then begin
  523.     { If we are not loading on demand, set the cursor to an hourglass }
  524.     OldCursor := Screen.Cursor;
  525.     if not EnableCaching then
  526.       Screen.Cursor := crHourGlass;
  527.     { Find out how many icons are in the file }
  528.     {$IFDEF WIN32}
  529.       FNumberOfIcons := ExtractIcon(hInstance, StrPCopy(Buff, FileName), -1);
  530.     {$ELSE}
  531.       FNumberOfIcons := ExtractIcon(hInstance, StrPCopy(Buff, FileName), word(-1));
  532.     {$ENDIF}
  533.     { Loop for every icon in the file }
  534.     for x := 0 to NumberOfIcons - 1 do begin
  535.       { If we are not loading on demand... }
  536.       if not EnableCaching then begin
  537.         { Create a TIcon object... }
  538.         Icon := TIcon.Create;
  539.         { and assign the icon to it. }
  540.         Icon.Handle := ExtractIcon(hInstance, Buff, x);
  541.         { Add the icon and a dummy string to the combobox }
  542.         Items.AddObject(Format('%d',[x]), Icon);
  543.       end else
  544.         { We're loading on demand, so just add a dummy string }
  545.         Items.AddObject(Format('%d',[x]), NIL);
  546.     end;
  547.     { Reset the index to the first item. }
  548.     ItemIndex := 0;
  549.     { if not loading on demand, restore the cursor }
  550.     if not EnableCaching then
  551.       Screen.Cursor := OldCursor;
  552.   end;
  553. end;
  554.  
  555. { Free the icon resources we created. }
  556. procedure TIconListBox.FreeIcons;
  557. var
  558.   x: integer;
  559.   Icon: TIcon;
  560. begin
  561.   { Loop for every icon }
  562.   for x := 0 to Items.Count-1 do begin
  563.     { Get the icon object }
  564.     Icon := TIcon(Items.Objects[x]);  { Don't use GetIcon here! }
  565.     { Free it.  If it is NIL, Free ignores it, so it is safe }
  566.     Icon.Free;
  567.     { Zero out the TIcon we just freed }
  568.     Items.Objects[x] := NIL;
  569.   end;
  570.   { Reset the number of Icons to reflect that we have no file. }
  571.   FNumberOfIcons := -1;
  572. end;
  573.  
  574. { Disable the control if we don't have a valid filename, and option is enabled }
  575. procedure TIconListBox.UpdateEnabledState;
  576. begin
  577.   if AutoDisable then
  578.     Enabled := FileExists(FileName)
  579.   else
  580.     Enabled := TRUE;
  581. end;
  582.  
  583. { Reset the size of the listbox to reflect changes in orientation and IconsDisplayed }
  584. procedure TIconListBox.ResetSize;
  585. begin
  586.   Height := ItemHeight * YIcons + GetSystemMetrics(SM_CYHSCROLL) + 1;
  587.   Width := FItemWidth * XIcons + 2;
  588.   Columns := XIcons;
  589. (*  if Orientation = lbVertical then begin
  590.     { Set height to hold the desired number of icons }
  591.     Height := ItemHeight * IconsDisplayed + 2;
  592.     { Set width to an icon plus a scrollbar }
  593.     Width := FItemWidth + GetSystemMetrics(SM_CXVSCROLL) + 10;
  594.     { Make sure we don't have any columns. }
  595.     Columns := 0;
  596.   end else begin
  597.     { Set height to an icon plus a scrollbar }
  598.     Height := ItemHeight + GetSystemMetrics(SM_CYHSCROLL) + 1;
  599.     { Set width to hold the desired number of icons }
  600.     Width := FItemWidth * IconsDisplayed + 2;
  601.     { Set number of columns in the listbox to the desired number of icons }
  602.     Columns := IconsDisplayed;
  603.   end;*)
  604. end;
  605.  
  606. { Update the filename of the icon file. }
  607. procedure TIconListBox.SetFileName(Value: String);
  608. begin
  609.   { If new value is same as old, don't reload icons.  That's silly. }
  610.   if FFileName = Value then exit;
  611.   FFileName := Value;
  612.   { Initialize icon handles from new icon file. }
  613.   LoadIcons;
  614.   { Call user event handler, if one exists }
  615.   if assigned(FOnFileChange) then
  616.     FOnFileChange(Self);
  617. end;
  618.  
  619. { Update the AutoDisable property }
  620. procedure TIconListBox.SetAutoDisable(Value: boolean);
  621. begin
  622.   { If it's the same, we don't need to do anything }
  623.   if Value = FAutoDisable then exit;
  624.   FAutoDisable := Value;
  625.   { Update the enabled state of control based on new AutoDisable setting }
  626.   UpdateEnabledState;
  627. end;
  628.  
  629. { Update the EnableCaching property }
  630. procedure TIconListBox.SetEnableCaching(Value: boolean);
  631. begin
  632.   { If it's the same, we don't need to do anything }
  633.   if Value = FEnableCaching then exit;
  634.   FEnableCaching := Value;
  635.   { If load on demand is not enabled, we need to load all the icons. }
  636.   if not FEnableCaching then
  637.     LoadIcons;
  638. end;
  639.  
  640. { Set the number of icons to be displayed in the listbox }
  641. procedure TIconListBox.SetXIcons(Value: integer);
  642. begin
  643.   { If number hasn't changed then don't do anything }
  644.   if (Value = FXIcons) or (Value < 1) then exit;
  645.   FXIcons:= Value;
  646.   { Call ResetSize to update the width or height, depending on the orientation }
  647.   ResetSize;
  648. end;
  649.  
  650. procedure TIconListBox.SetYIcons(Value: integer);
  651. begin
  652.   { If number hasn't changed then don't do anything }
  653.   if (Value = FYIcons) or (Value < 1) then exit;
  654.   FYIcons := Value;
  655.   { Call ResetSize to update the width or height, depending on the orientation }
  656.   ResetSize;
  657. end;
  658.  
  659. { Used to extract icons from files and assign them to a TIcon object }
  660. function TIconListBox.ReadIcon(const Index: integer): TIcon;
  661. var
  662.   Buff: array[0..255] of char;
  663. begin
  664.   { Create the new icon }
  665.   Result := TIcon.Create;
  666.   { Assign it the icon handle }
  667.   Result.Handle := ExtractIcon(hInstance, StrPCopy(Buff, FileName), Index);
  668. end;
  669.  
  670. { Returns the icon for a given combobox index }
  671. function TIconListBox.GetIcon(const Index: integer): TIcon;
  672. begin
  673.   { If load on demand is enabled... }
  674.   if EnableCaching then
  675.     { Has the icon been loaded yet? }
  676.     if Items.Objects[Index] = NIL then
  677.       { No, we must get the icon and add it to Objects }
  678.       Items.Objects[Index] := ReadIcon(Index);
  679.   { Return the requested icon }
  680.   Result := TIcon(Items.Objects[Index]);
  681. end;
  682.  
  683. { Return the size of the item we are drawing }
  684. procedure TIconListBox.MeasureItem(Index: Integer; var Height: Integer);
  685. begin
  686.   { Ask Windows how tall icons are }
  687.   Height := GetSystemMetrics(SM_CYICON);
  688. end;
  689.  
  690. { Draw the item requested in the given rectangle.  Because of the parent's default }
  691. { behavior, we needn't worry about the State.  That's very nice.                   }
  692. procedure TIconListBox.DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState);
  693. var
  694.   Icon: TIcon;
  695. begin
  696.   { Use the controls canvas for drawing... }
  697.   with Canvas do begin
  698.     try
  699.       { Fill in the rectangle.  The proper brush has already been set up for us,   }
  700.       { so we needn't use State to set it ourselves.                               }
  701.       FillRect(Rect);
  702.       { Get the icon to be drawn }
  703.       Icon := GetIcon(Index);
  704.       { If nothing has gone wrong, draw the icon.  Theoretically, it should never  }
  705.       { be NIL, but why take the chance?                                           }
  706.       if Icon <> nil then
  707.         { Using the given rectangle, draw the icon on the control's canvas,        }
  708.         { centering it within the rectangle.                                       }
  709.         with Rect do Draw(Left + (Right - Left - Icon.Width) div 2,
  710.                           Top + (Bottom - Top - Icon.Width) div 2, Icon);
  711.     except
  712.       { If anything went wrong, we fall down to here.  You may want to add some    }
  713.       { sort of user notification.  No clean up is necessary since we did not      }
  714.       { create anything.  We'll just ignore the problem and hope it goes away. :)  }
  715.       {!};
  716.     end;
  717.   end;
  718. end;
  719.  
  720.  
  721. { Add the components to the Delphi Component Palette.  You will want to modify     }
  722. { this so that it appears on the page of your choice.                              }
  723. procedure Register;
  724. begin
  725.   RegisterComponents('Components R Us', [TIconComboBox, TIconListBox]);
  726. end;
  727.  
  728. end.
  729.