PathComboBox.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. unit PathComboBox;
  2. // -----------------------------------------------------------------------------
  3. // Project: Shell PathComboBox Component
  4. // Component Names: TPathComboBox
  5. // Module: PathComboBox
  6. // Version: 3.2
  7. // Date: 03-MAY-1999
  8. // Target: Win32; Delphi3-6, C++ Builder 3-5
  9. // Author: Angus Johnson, ajohnson@rpi.net.au
  10. // Copyright ©1997-99 Angus Johnson
  11. // -----------------------------------------------------------------------------
  12. {$include dragdrop.inc}
  13. interface
  14. uses
  15. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  16. StdCtrls, ShellApi, ShlObj, ActiveX {$IFDEF VER12_PLUS},ImgList{$ENDIF};
  17. type
  18. TPathComboBox = class(TCustomComboBox)
  19. private
  20. FPath: string;
  21. FDisplayName: string;
  22. FPidl: pItemIdList;
  23. DesktopPIDL,DrivesPIDL: pItemIdList;
  24. FAllowVirtual: boolean; //? allow 'Control Panel', 'Printers' etc.
  25. FIsVirtualFolder: boolean;
  26. FImageList: TImageList;
  27. FDrawingEdit: boolean;
  28. //Can't use items.objects to store ItemData (in Delphi 3) because no
  29. //handle to 'Items' exists in Destroy method (where they are cleaned up)...
  30. //(In Delphi 4, the BeforeDestruction method could be used instead.)
  31. FItemDataList: TList;
  32. procedure BuildCore;
  33. procedure ClearNonCore;
  34. procedure SetPath(NewPath: string);
  35. procedure SetPidl(pidl: pItemIdList);
  36. procedure SortItems(StartItem, EndItem: integer);
  37. procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  38. procedure WMKEYDOWN(var Message: TWMKey); message WM_KEYDOWN;
  39. procedure CNCommand(var Message: TWMCommand); message CN_COMMAND;
  40. procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
  41. protected
  42. procedure CreateWnd; override;
  43. procedure BuildNewList(pidl: pItemIdList);
  44. procedure DrawItem(Index: Integer;
  45. Rect: TRect; State: TOwnerDrawState); override;
  46. public
  47. constructor Create(AOwner: TComponent); override;
  48. destructor Destroy; override;
  49. procedure Change; override;
  50. //The Path properties...
  51. //The path can be set with either a string or a PIDL (pItemIdList) ...
  52. property Path: string read FPath write SetPath;
  53. property Pidl: pItemIdList read FPidl write SetPidl;
  54. //The Folder display name (which is different to the Path).
  55. property DisplayName: string read FDisplayName;
  56. //True if 'Virtual' folder selected (Control Panel, Printers etc)
  57. //nb: if IsVirtualPath = true then Path = ''.
  58. property IsVirtualPath: boolean read FIsVirtualFolder;
  59. published
  60. property Color;
  61. property Ctl3D;
  62. property DragMode;
  63. property DragCursor;
  64. property Enabled;
  65. property Font;
  66. property ParentColor;
  67. property ParentCtl3D;
  68. property ParentFont;
  69. property ParentShowHint;
  70. property PopupMenu;
  71. property ShowHint;
  72. property ShowVirtualFolders: boolean read FAllowVirtual write FAllowVirtual;
  73. property TabOrder;
  74. property TabStop;
  75. property Visible;
  76. property OnClick;
  77. property OnChange;
  78. property OnDblClick;
  79. property OnDragDrop;
  80. property OnDragOver;
  81. property OnDropDown;
  82. property OnEndDrag;
  83. property OnEnter;
  84. property OnExit;
  85. property OnKeyDown;
  86. property OnKeyPress;
  87. property OnKeyUp;
  88. property OnStartDrag;
  89. end;
  90. procedure Register;
  91. //---------------------------------------------------------------------
  92. // Some 'unnamed' Windows functions (which are very useful) ...
  93. // (Thanks to - http://www.geocities.com/SiliconValley/4942/index.html)
  94. //---------------------------------------------------------------------
  95. function ILCombine(pidl1,pidl2:PItemIDList): PItemIDList; stdcall;
  96. function ILFindLastID(pidl: PItemIDList): PItemIDList; stdcall;
  97. function ILClone(pidl: PItemIDList): PItemIDList; stdcall;
  98. function ILRemoveLastID(pidl: PItemIDList): LongBool; stdcall;
  99. function ILIsEqual(pidl1,pidl2: PItemIDList): LongBool; stdcall;
  100. var
  101. //The following 2 interface pointers are declared in the 'interface' section
  102. //as they may be very useful. They are assigned in 'initialization' section.
  103. DesktopShellFolder: IShellFolder;
  104. //ShellMalloc is used in this unit just to free 'Pidls'.
  105. //Probably _slightly_ quicker than using CoTaskMemFree() each time.
  106. ShellMalloc: IMalloc;
  107. implementation
  108. //---------------------------------------------------------------------
  109. // Miscellaneous Functions ...
  110. //---------------------------------------------------------------------
  111. procedure Register;
  112. begin
  113. RegisterComponents('Samples', [TPathComboBox]);
  114. end;
  115. //---------------------------------------------------------------------
  116. function ILCombine(pidl1,pidl2:PItemIDList): PItemIDList; stdcall;
  117. external shell32 index 25;
  118. function ILFindLastID(pidl: PItemIDList): PItemIDList; stdcall;
  119. external shell32 index 16;
  120. function ILClone(pidl: PItemIDList): PItemIDList; stdcall;
  121. external shell32 index 18;
  122. function ILRemoveLastID(pidl: PItemIDList): LongBool; stdcall;
  123. external shell32 index 17;
  124. function ILIsEqual(pidl1,pidl2: PItemIDList): LongBool; stdcall;
  125. external shell32 index 21;
  126. //---------------------------------------------------------------------
  127. function GetFullPathFromPidl(pidl: pItemIDList): string;
  128. var
  129. buff: pChar;
  130. begin
  131. GetMem(buff,MAX_PATH);
  132. if SHGetPathFromIDList(pidl,buff) then
  133. result := buff else
  134. result := '';
  135. FreeMem(buff);
  136. end;
  137. //---------------------------------------------------------------------
  138. function GetPidlFromPath(Folder: string): pItemIdList;
  139. var
  140. dummy,dummy2: DWORD;
  141. WideStr: WideString;
  142. begin
  143. WideStr := Folder;
  144. //nb: DesktopShellFolder is defined as a Global Variable.
  145. if FAILED(DesktopShellFolder.ParseDisplayName(0,
  146. nil,PWideChar(WideStr),dummy,result,dummy2)) then result := nil;
  147. end;
  148. //---------------------------------------------------------------------
  149. // TItemData class
  150. // (Used to store extra data with each Combobox dropdown item)
  151. //---------------------------------------------------------------------
  152. type
  153. TItemData = class
  154. Foldername: string;
  155. SortString: string; //used just to sort the "core" folder items
  156. Level: integer;
  157. Core: boolean; // flag for "core" folder items
  158. ImageIndex: integer;
  159. ImageIndexOpen: integer;
  160. RelativePIDL: pItemIDList; {each item stores its own PIDLs}
  161. AbsolutePIDL: pItemIDList;
  162. public
  163. destructor Destroy; override;
  164. end;
  165. destructor TItemData.Destroy;
  166. begin
  167. ShellMalloc.Free(AbsolutePIDL);
  168. inherited;
  169. end;
  170. //---------------------------------------------------------------------
  171. // TPathComboBox Component ...
  172. //---------------------------------------------------------------------
  173. constructor TPathComboBox.Create(AOwner: TComponent);
  174. var
  175. sfi : tshfileinfo;
  176. begin
  177. inherited Create(AOwner);
  178. width := 230;
  179. Style := csOwnerDrawFixed;
  180. FAllowVirtual := false;
  181. FImageList := TImageList.create(self);
  182. //get the shell imagelist...
  183. if not (csDesigning in ComponentState) then
  184. begin
  185. FImageList.handle := shgetfileinfo('',0,
  186. sfi,sizeof(tshfileinfo), shgfi_sysiconindex or shgfi_smallicon);
  187. FImageList.shareimages := true;
  188. FImageList.BlendColor := clHighlight;
  189. FImageList.DrawingStyle := dsTransparent;
  190. end;
  191. FItemDataList := TList.create;
  192. end;
  193. //---------------------------------------------------------------------
  194. procedure TPathComboBox.CreateWnd;
  195. begin
  196. inherited;
  197. if not (csDesigning in ComponentState) then
  198. BuildCore;
  199. end;
  200. //---------------------------------------------------------------------
  201. destructor TPathComboBox.Destroy;
  202. var
  203. i: integer;
  204. begin
  205. FImageList.handle := 0;
  206. FImageList.free;
  207. for i := 0 to FItemDataList.count-1 do
  208. TItemData(FItemDataList[i]).free;
  209. FItemDataList.free;
  210. inherited Destroy;
  211. end;
  212. //---------------------------------------------------------------------
  213. procedure TPathComboBox.SortItems(StartItem, EndItem: integer);
  214. var
  215. i: integer;
  216. procedure Swap(a,b: integer);
  217. var
  218. str: string;
  219. begin
  220. str := items[a];
  221. items[a] := items[b];
  222. items[b] := str;
  223. FItemDataList.exchange(a,b);
  224. end;
  225. begin
  226. while EndItem > StartItem do
  227. begin
  228. for i := StartItem to EndItem-1 do
  229. if TItemData(FItemDataList[i]).SortString >
  230. TItemData(FItemDataList[i+1]).SortString then swap(i,i+1);
  231. dec(EndItem);
  232. end;
  233. end;
  234. //---------------------------------------------------------------------
  235. type
  236. TAllowed = (aALL, aSYSTEM);
  237. procedure TPathComboBox.BuildCore;
  238. var
  239. sfi : tshfileinfo;
  240. DrivesShellFolder: IShellFolder;
  241. pidl,absPidl: pItemIdList;
  242. EnumIdList: IEnumIdList;
  243. SuccessCnt: DWORD;
  244. i, SortStartItem: integer;
  245. procedure AddToList(pidl: pItemIdList; ItemLevel: integer; allowed: TAllowed);
  246. var
  247. ItemData: TItemData;
  248. tmpPath: string;
  249. begin
  250. //nb: the 'pidl' passed as a parameter is freed when ItemData is freed.
  251. ShGetFileInfo(PChar(pidl), 0,sfi,sizeof(sfi),
  252. {SHGFI_ATTRIBUTES or} SHGFI_ICON or SHGFI_DISPLAYNAME or SHGFI_PIDL);
  253. if (allowed = aSYSTEM) and (sfi.dwAttributes and SFGAO_FILESYSTEM = 0) then
  254. begin
  255. //don't add this (virtual) item!
  256. ShellMalloc.Free(pidl);
  257. exit;
  258. end;
  259. tmpPath := GetFullPathFromPidl(pidl);
  260. ItemData := TItemData.create;
  261. with ItemData do
  262. begin
  263. AbsolutePIDL := pidl;
  264. if Level = 0 then //desktop
  265. RelativePIDL := AbsolutePIDL else
  266. RelativePIDL := ILFindLastID(AbsolutePIDL);
  267. Foldername := sfi.szDisplayName;
  268. //SortString is used to sort the "Drives" folder and Desktop folders...
  269. //Virtual folders follow system folders in the "Drives" folder,
  270. //Virtual folders go before system folders in the "Desktop" folder.
  271. if tmpPath <> '' then //file system folders
  272. begin
  273. if ItemLevel = 1 then
  274. SortString := '9'+tmpPath else
  275. SortString := '1'+tmpPath;
  276. end else
  277. begin //virtual folders
  278. if ItemLevel = 1 then
  279. SortString := '1'+ Foldername else
  280. SortString := '9'+ Foldername;
  281. end;
  282. ImageIndex := sfi.iIcon;
  283. ImageIndexOpen := sfi.iIcon;
  284. Level := ItemLevel;
  285. Core := true;
  286. end;
  287. items.Add(tmpPath);
  288. FItemDataList.add(ItemData);
  289. end;
  290. begin
  291. //if just handle being reallocated then rebuild everything...
  292. if FItemDataList.count > 0 then
  293. begin
  294. items.clear;
  295. for i := 0 to FItemDataList.count-1 do
  296. with TItemData(FItemDataList[i]) do
  297. begin
  298. items.add(GetFullPathFromPidl(AbsolutePIDL));
  299. if AbsolutePIDL = FPidl then itemindex := i;
  300. end;
  301. if itemindex < 0 then itemindex := 0;
  302. exit;
  303. end;
  304. //BUILD THE CORE ITEMS...
  305. if FAILED(SHGetDesktopFolder(DesktopShellFolder)) then
  306. raise Exception.Create('Unable to create "DesktopShellFolder" in PathComboBox');
  307. if FAILED(SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,DesktopPIDL)) then
  308. raise Exception.Create('Unable to create "DesktopPIDL" in PathComboBox');
  309. if FAILED(SHGetSpecialFolderLocation(0,CSIDL_DRIVES,DrivesPIDL)) then
  310. raise Exception.Create('Unable to create "DrivesPIDL" in PathComboBox');
  311. //Add desktop ...
  312. AddToList(DesktopPIDL,0,aALL);
  313. //default to desktop...
  314. itemindex := 0;
  315. FIsVirtualFolder := false;
  316. FPidl := DesktopPIDL;
  317. fPath := items[0];
  318. FDisplayName := TItemData(FItemDataList[0]).Foldername;
  319. //Add drives folder...
  320. AddToList(DrivesPIDL,1,aALL);
  321. //Add Drives sub-folders ...
  322. if FAILED(DesktopShellFolder.BindToObject(DrivesPIDL,
  323. nil, IID_IShellFolder, pointer(DrivesShellFolder))) then
  324. raise Exception.Create('Unable to create "DrivesShellFolder" in PathComboBox');
  325. if FAILED(DrivesShellFolder.EnumObjects(0,SHCONTF_FOLDERS,EnumIdList)) then
  326. raise Exception.Create('Unable to enumerate "Drives" Folder in PathComboBox');
  327. //Enumerating more than 1 at a time doesn't seem to work!?
  328. while SUCCEEDED(EnumIdList.Next(1,pidl,SuccessCnt)) and (SuccessCnt = 1) do
  329. begin
  330. absPidl := ILCombine(DrivesPIDL,Pidl);
  331. ShellMalloc.Free(pidl);
  332. if FAllowVirtual then
  333. AddToList(absPidl,2,aALL) else
  334. AddToList(absPidl,2,aSYSTEM);
  335. end;
  336. SortItems(2,items.count-1); //sort the "drives" folder...
  337. SortStartItem := items.Count; //Position for start of next sort
  338. //Now enumerate remaining Desktop sub-folders skipping the 'Drives' folder
  339. //which has already been added (file system folders added last).
  340. DesktopShellFolder.EnumObjects(0,SHCONTF_FOLDERS,EnumIdList);
  341. while SUCCEEDED(EnumIdList.Next(1,pidl,SuccessCnt)) and (SuccessCnt = 1) do
  342. begin
  343. absPidl := ILCombine(DesktopPIDL,pidl);
  344. ShellMalloc.Free(pidl);
  345. if ILIsEqual(absPidl,DrivesPIDL) then
  346. ShellMalloc.Free(absPidl) else
  347. AddToList(absPidl,1,aALL);
  348. end;
  349. SortItems(SortStartItem,items.count-1); //sort the remaining "desktop" folders...
  350. end;
  351. //---------------------------------------------------------------------
  352. procedure TPathComboBox.ClearNonCore;
  353. var
  354. i: integer;
  355. begin
  356. for i := FItemDataList.count-1 downto 2 do
  357. with TItemData(FItemDataList[i]) do
  358. if not Core then
  359. begin
  360. if i < items.count then items.delete(i);
  361. free;
  362. FItemDataList.delete(i);
  363. end;
  364. end;
  365. //---------------------------------------------------------------------
  366. procedure TPathComboBox.CNCommand(var Message: TWMCommand);
  367. begin
  368. inherited;
  369. //when the dropdown window closes notify of (potential) change...
  370. if Message.NotifyCode = CBN_CLOSEUP then Change;
  371. end;
  372. //---------------------------------------------------------------------
  373. procedure TPathComboBox.Change;
  374. begin
  375. //don't process any changes while dropdown window visible...
  376. if (sendmessage(handle,CB_GETDROPPEDSTATE,0,0) <> 0) then exit;
  377. //OK, only do something if the path has changed...
  378. if (itemindex >= 0) and ((FPidl = nil) or
  379. not ILIsEqual(TItemData(FItemDataList[itemindex]).AbsolutePIDL,FPidl)) then
  380. begin
  381. SetPidl(TItemData(FItemDataList[itemindex]).AbsolutePIDL);
  382. inherited Change;
  383. end;
  384. end;
  385. //---------------------------------------------------------------------
  386. procedure TPathComboBox.SetPath(NewPath: string);
  387. var
  388. tmpPidl: pItemIdList;
  389. begin
  390. tmpPidl := GetPidlFromPath(NewPath);
  391. if tmpPidl = nil then exit;
  392. SetPidl(tmpPidl);
  393. ShellMalloc.Free(tmpPidl);
  394. end;
  395. //---------------------------------------------------------------------
  396. //NB: A 'virtual' path can only be set by its PIDL...
  397. procedure TPathComboBox.SetPidl(pidl: pItemIdList);
  398. var
  399. tmpPidl: pItemIdList;
  400. begin
  401. //clone 'pidl' incase it is destroyed during BuildNewList...
  402. HandleNeeded;
  403. tmpPidl := ILClone(pidl);
  404. BuildNewList(tmpPidl);
  405. ShellMalloc.Free(tmpPidl);
  406. if assigned(OnChange) then OnChange(self);
  407. end;
  408. //---------------------------------------------------------------------
  409. procedure TPathComboBox.BuildNewList(pidl: pItemIdList);
  410. var
  411. tmpList: TList;
  412. i,j: integer;
  413. tmpPidl,tmpPidl2: pItemIdList;
  414. found: boolean;
  415. procedure CleanUp;
  416. var
  417. i: integer;
  418. begin
  419. for i := 0 to tmpList.count-1 do
  420. ShellMalloc.Free(pItemIdList(tmpList[i]));
  421. tmpList.free;
  422. end;
  423. procedure AddSubFolders(StartLevel,InsertPos: integer);
  424. var
  425. i: integer;
  426. sfi : tshfileinfo;
  427. ItemData: TItemData;
  428. begin
  429. for i := StartLevel to tmpList.count-1 do
  430. begin
  431. tmpPidl := tmpList[tmpList.count-i-1];
  432. ShGetFileInfo(PChar(tmpPidl),
  433. 0,sfi,sizeof(sfi),SHGFI_ICON or SHGFI_DISPLAYNAME or SHGFI_PIDL);
  434. ItemData := TItemData.create;
  435. with ItemData do
  436. begin
  437. AbsolutePIDL := ILClone(tmpPidl);
  438. RelativePIDL := ILFindLastID(AbsolutePIDL);
  439. Foldername := sfi.szDisplayName;
  440. Core := false;
  441. ImageIndex := sfi.iIcon;
  442. Level := i;
  443. ShGetFileInfo(PChar(AbsolutePIDL),0,sfi,sizeof(sfi),
  444. SHGFI_SYSICONINDEX or SHGFI_OPENICON or SHGFI_PIDL);
  445. ImageIndexOpen := sfi.iIcon;
  446. end;
  447. FItemDataList.insert(InsertPos,ItemData);
  448. items.insert(InsertPos,GetFullPathFromPidl(ItemData.AbsolutePIDL));
  449. inc(InsertPos);
  450. end;
  451. with TItemData(FItemDataList[InsertPos-1]) do
  452. begin
  453. fPidl := AbsolutePIDL;
  454. FDisplayName := Foldername;
  455. itemindex := InsertPos-1;
  456. fPath := items[itemindex];
  457. end;
  458. end;
  459. begin
  460. ClearNonCore;
  461. tmpPidl := ILClone(pidl);
  462. tmpPidl2 := ILClone(pidl);
  463. //create a list of absolute pidls from the path...
  464. tmpList := TList.create;
  465. tmpList.add(tmpPidl2);
  466. while ILRemoveLastID(tmpPidl) do
  467. begin
  468. tmpPidl2 := ILClone(tmpPidl);
  469. tmpList.add(tmpPidl2);
  470. end;
  471. ShellMalloc.Free(tmpPidl);
  472. FPidl := nil;
  473. fPath := '';
  474. //If the pidl is part of the core, then not much to do ...
  475. for i := 0 to FItemDataList.count-1 do
  476. if ILIsEqual( TItemData(FItemDataList[i]).AbsolutePIDL, pidl ) then
  477. begin
  478. FPidl := TItemData(FItemDataList[i]).AbsolutePIDL;
  479. FDisplayName := TItemData(FItemDataList[i]).Foldername;
  480. itemindex := i;
  481. fPath := items[itemindex];
  482. CleanUp;
  483. exit;
  484. end;
  485. found := false;
  486. //find the matching Level1 folder...
  487. for i := 1 to FItemDataList.count -1 do
  488. if ILIsEqual( tmpList[tmpList.count-2],
  489. TItemData(FItemDataList[i]).AbsolutePIDL) then
  490. begin
  491. //if the Level1 folder is the 'Drives' folder...
  492. //find the matching Level2 folder...
  493. if ILIsEqual( tmpList[tmpList.count-2],DrivesPIDL) then
  494. begin
  495. for j := 2 to FItemDataList.count -1 do
  496. if ILIsEqual( tmpList[tmpList.count-3],
  497. TItemData(FItemDataList[j]).AbsolutePIDL) then
  498. begin
  499. AddSubFolders(3,j+1);
  500. found := true;
  501. break;
  502. end;
  503. end else
  504. begin
  505. AddSubFolders(2,i+1);
  506. found := true;
  507. break;
  508. end;
  509. end;
  510. cleanup;
  511. if not found then
  512. raise Exception.create('Invalid PIDL passed to PathComboBox');
  513. end;
  514. //---------------------------------------------------------------------
  515. procedure TPathComboBox.CNDrawItem(var Message: TWMDrawItem);
  516. begin
  517. with Message.DrawItemStruct^ do
  518. begin
  519. //Delphi doesn't indicate in TOwnerDrawState whether it's
  520. //the Edit or Dropdown window that's about to painted so...
  521. FDrawingEdit := (itemState and ODS_COMBOBOXEDIT) <> 0;
  522. //A little workaround so the component name will be drawn if designing.
  523. if (Integer(itemID) < 0) then itemID := $FFFF;
  524. inherited;
  525. end;
  526. end;
  527. //---------------------------------------------------------------------
  528. procedure TPathComboBox.DrawItem(Index: Integer;
  529. Rect: TRect; State: TOwnerDrawState);
  530. begin
  531. with canvas do
  532. begin
  533. FillRect(Rect);
  534. //if there are no items, ie designing ...
  535. if Index = $FFFF then
  536. begin
  537. textout(Rect.left+2, Rect.top+1, Name);
  538. exit;
  539. end;
  540. with TItemData(FItemDataList[Index]) do
  541. begin
  542. if FDrawingEdit then
  543. Rect.left := Rect.left + 2 else
  544. Rect.left := Rect.left + 2 + (Level*12);
  545. Rect.top := Rect.top + 1;
  546. if (AbsolutePIDL = FPidl) then
  547. FImageList.draw(canvas,Rect.left,Rect.top,ImageIndexOpen) else
  548. FImageList.draw(canvas,Rect.left,Rect.top,ImageIndex);
  549. Rect.left := Rect.left+FImageList.width+4;
  550. textout(Rect.left, Rect.top, Foldername);
  551. end;
  552. end;
  553. end;
  554. //---------------------------------------------------------------------
  555. procedure TPathComboBox.WMKEYDOWN(var Message: TWMKey);
  556. begin
  557. //Only process keys when the dropdown window is visible.
  558. //note: alt-downarrow etc still work as expected...
  559. if (sendmessage(handle,CB_GETDROPPEDSTATE,0,0) <> 0) then inherited;
  560. end;
  561. //---------------------------------------------------------------------
  562. //Make sure there is still room for the image if small fonts are used...
  563. procedure TPathComboBox.CMFontChanged(var Message: TMessage);
  564. //borrowed from Delphi :-)
  565. function GetItemHeight(Font: TFont): Integer;
  566. var
  567. DC: HDC;
  568. SaveFont: HFont;
  569. Metrics: TTextMetric;
  570. begin
  571. DC := GetDC(0);
  572. SaveFont := SelectObject(DC, Font.Handle);
  573. GetTextMetrics(DC, Metrics);
  574. SelectObject(DC, SaveFont);
  575. ReleaseDC(0, DC);
  576. Result := Metrics.tmHeight;
  577. end;
  578. var
  579. IHeight: integer;
  580. begin
  581. inherited;
  582. IHeight := GetItemHeight(Font);
  583. if IHeight < FImageList.height then IHeight := FImageList.height;
  584. ItemHeight := IHeight+2;
  585. RecreateWnd;
  586. end;
  587. //---------------------------------------------------------------------
  588. //---------------------------------------------------------------------
  589. initialization
  590. SHGetDesktopFolder(DesktopShellFolder);
  591. ShGetMalloc(ShellMalloc);
  592. end.