IEPathComboBox.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. unit IEPathComboBox;
  2. interface
  3. {$WARN UNIT_PLATFORM OFF}
  4. uses
  5. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  6. StdCtrls, CommCtrl, ShellAPI, ImgList, ShlObj,
  7. CustomPathComboBox, IEDriveInfo, IEComboBox, DirView;
  8. type
  9. TDirectoryToSelect = (dsCurrent, dsRoot);
  10. TDriveType = (dtUnknown, dtNoRootDrive, dtFloppy, dtFixed, dtRemote, dtCDROM, dtRAM);
  11. TDriveTypes = set of TDriveType;
  12. TSpecialFolder = (sfDesktop, sfMyDocuments);
  13. TSpecialFolders = set of TSpecialFolder;
  14. const
  15. DefaultDriveTypes = [dtFloppy, dtFixed, dtRemote, dtCDROM, dtRAM];
  16. DefaultSpecialFolders = [sfDesktop, sfMyDocuments];
  17. type
  18. TFolderInfo = record
  19. Valid: Boolean;
  20. Path: string;
  21. DisplayName: string;
  22. ImageIndex: Integer;
  23. Text: string;
  24. PIDL: PItemIDList;
  25. end;
  26. type
  27. TIEPathComboBox = class(TCustomPathComboBox)
  28. private
  29. FDirectoryToSelect: TDirectoryToSelect;
  30. FDrive: TDrive;
  31. FDisplayStyle: TVolumeDisplayStyle;
  32. FDriveTypes: TDriveTypes;
  33. FDontNotifyPathChange: Boolean;
  34. FInternalWindowHandle: HWND;
  35. FShowSpecialFolders: TSpecialFolders;
  36. FSpecialFolders: array[TSpecialFolder] of TFolderInfo;
  37. procedure SetDisplayStyle(Value: TVolumeDisplayStyle);
  38. procedure SetDrive(Value: TDrive);
  39. function DriveStored: Boolean;
  40. function GetFocusedDrive: Char;
  41. function GetItemDrive(Index: Integer): TDrive;
  42. procedure SetShowSpecialFolders(Value: TSpecialFolders);
  43. protected
  44. procedure CreateWnd; override;
  45. procedure DropDown; override;
  46. procedure SetDriveTypes(Value: TDriveTypes); virtual;
  47. function GetItemImage(Index: Integer): Integer; override;
  48. function GetItemTextEx(Index: Integer; ForList: Boolean): string; override;
  49. procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  50. procedure PathChanged; override;
  51. procedure SetPath(Value: string); override;
  52. procedure InternalWndProc(var Msg: TMessage);
  53. function SpecialItems: Integer;
  54. procedure LoadFolderInfo(var Info: TFolderInfo);
  55. function GetItemSpecialFolder(Index: Integer): TSpecialFolder;
  56. property ItemDrive[Index: Integer]: TDrive read GetItemDrive;
  57. public
  58. constructor Create(AOwner: TComponent); override;
  59. destructor Destroy; override;
  60. function GetDriveIndex(Drive: TDrive; Closest: Boolean): Integer;
  61. procedure ResetItems;
  62. property FocusedDrive: Char read GetFocusedDrive;
  63. published
  64. property Drive: TDrive read FDrive write SetDrive
  65. stored DriveStored;
  66. property DirectoryToSelect: TDirectoryToSelect
  67. read FDirectoryToSelect write FDirectoryToSelect default dsRoot;
  68. property DisplayStyle: TVolumeDisplayStyle read fDisplayStyle
  69. write SetDisplayStyle default doPrettyName;
  70. property DriveTypes: TDriveTypes
  71. read FDriveTypes write SetDriveTypes default DefaultDriveTypes;
  72. property ShowSpecialFolders: TSpecialFolders
  73. read FShowSpecialFolders write SetShowSpecialFolders default DefaultSpecialFolders;
  74. property DropDownFixedWidth;
  75. property OnCloseUp;
  76. property ShowFullPath;
  77. property Align;
  78. property Anchors;
  79. property BiDiMode;
  80. property Color;
  81. property Constraints;
  82. property Ctl3D;
  83. property DragCursor;
  84. property DragKind;
  85. property DragMode;
  86. property DropDownCount;
  87. property Enabled;
  88. property Font;
  89. property ImeMode;
  90. property ImeName;
  91. property ParentBiDiMode;
  92. property ParentColor;
  93. property ParentCtl3D;
  94. property ParentFont;
  95. property ParentShowHint;
  96. property PopupMenu;
  97. property ShowHint;
  98. property TabOrder;
  99. property TabStop;
  100. property Visible;
  101. property OnChange;
  102. property OnClick;
  103. property OnDblClick;
  104. property OnDragDrop;
  105. property OnDragOver;
  106. property OnDrawItem;
  107. property OnDropDown;
  108. property OnEndDock;
  109. property OnEndDrag;
  110. property OnEnter;
  111. property OnExit;
  112. property OnKeyDown;
  113. property OnKeyPress;
  114. property OnKeyUp;
  115. property OnStartDock;
  116. property OnStartDrag;
  117. end;
  118. procedure Register;
  119. implementation
  120. uses
  121. Math, CustomDirView, BaseUtils;
  122. resourcestring
  123. SSpecialFolderMyDocuments = 'My documents';
  124. SSpecialFolderDesktop = 'Desktop';
  125. constructor TIEPathComboBox.Create(AOwner : TComponent);
  126. var
  127. InitPath: string;
  128. F: TSpecialFolder;
  129. begin
  130. inherited;
  131. UseSystemImageList := True;
  132. FDirectoryToSelect := dsRoot;
  133. FShowSpecialFolders := DefaultSpecialFolders;
  134. for F := Low(FSpecialFolders) to High(FSpecialFolders) do
  135. begin
  136. FSpecialFolders[F].Valid := False;
  137. FSpecialFolders[F].PIDL := nil;
  138. end;
  139. SpecialFolderLocation(CSIDL_PERSONAL, FSpecialFolders[sfMyDocuments].Path,
  140. FSpecialFolders[sfMyDocuments].PIDL);
  141. FSpecialFolders[sfMyDocuments].Text := SSpecialFolderMyDocuments;
  142. SpecialFolderLocation(CSIDL_DESKTOP, FSpecialFolders[sfDesktop].Path,
  143. FSpecialFolders[sfDesktop].PIDL);
  144. FSpecialFolders[sfDesktop].Text := SSpecialFolderDesktop;
  145. InitPath := GetCurrentDir;
  146. if IsUNCPath(InitPath) then
  147. begin
  148. InitPath := UserDocumentDirectory;
  149. if IsUNCPath(InitPath) then
  150. begin
  151. InitPath := AnyValidPath;
  152. end;
  153. end;
  154. InitPath := ExtractFileDrive(InitPath);
  155. if (Length(InitPath) <> 2) or (InitPath[2] <> ':') then FDrive := FirstFixedDrive
  156. else FDrive := InitPath[1];
  157. FDriveTypes := DefaultDriveTypes;
  158. FDontNotifyPathChange := False;
  159. ResetItemHeight;
  160. FInternalWindowHandle := Classes.AllocateHWnd(InternalWndProc);
  161. end; {TIEPathComboBox.Create}
  162. destructor TIEPathComboBox.Destroy;
  163. begin
  164. Classes.DeallocateHWnd(FInternalWindowHandle);
  165. inherited;
  166. end;
  167. procedure TIEPathComboBox.InternalWndProc(var Msg: TMessage);
  168. begin
  169. with Msg do
  170. begin
  171. if (Msg = WM_DEVICECHANGE) and
  172. ((wParam = {DBT_CONFIGCHANGED} $0018) or (wParam = {DBT_DEVICEARRIVAL} $8000) or
  173. (wParam = {DBT_DEVICEREMOVECOMPLETE} $8004)) then
  174. begin
  175. try
  176. DriveInfo.Load;
  177. ResetItems;
  178. except
  179. Application.HandleException(Self);
  180. end
  181. end;
  182. Result := DefWindowProc(FInternalWindowHandle, Msg, wParam, lParam);
  183. end;
  184. end;
  185. procedure TIEPathComboBox.CreateWnd;
  186. begin
  187. inherited;
  188. ResetItems;
  189. end;
  190. function TIEPathComboBox.DriveStored: Boolean;
  191. begin
  192. Result := (not Assigned(DirView)) and (Drive <> FirstFixedDrive);
  193. end; { DriveStored }
  194. procedure TIEPathComboBox.DropDown;
  195. begin
  196. inherited;
  197. end;
  198. function TIEPathComboBox.SpecialItems: Integer;
  199. begin
  200. Result := 0;
  201. if sfDesktop in ShowSpecialFolders then Inc(Result);
  202. if sfMyDocuments in ShowSpecialFolders then Inc(Result);
  203. end;
  204. function TIEPathComboBox.GetItemSpecialFolder(Index: Integer): TSpecialFolder;
  205. begin
  206. if (Index = 0) and (sfDesktop in ShowSpecialFolders) then Result := sfDesktop
  207. else
  208. if ((Index = 0) or (Index = 1)) and (sfMyDocuments in ShowSpecialFolders) then Result := sfMyDocuments
  209. else Assert(False);
  210. end;
  211. procedure TIEPathComboBox.LoadFolderInfo(var Info: TFolderInfo);
  212. var
  213. FileInfo: TShFileInfo;
  214. Path: PChar;
  215. Flags: Word;
  216. begin
  217. if not Info.Valid then
  218. begin
  219. if Info.PIDL <> nil then
  220. begin
  221. Path := PChar(Info.PIDL);
  222. Flags := SHGFI_PIDL;
  223. end
  224. else
  225. begin
  226. Path := PChar(Info.Path);
  227. Flags := 0;
  228. end;
  229. SHGetFileInfo(Path, 0, FileInfo, SizeOf(FileInfo),
  230. SHGFI_DISPLAYNAME or SHGFI_SYSICONINDEX or SHGFI_SMALLICON or Flags);
  231. Info.ImageIndex := FileInfo.iIcon;
  232. Info.DisplayName := FileInfo.szDisplayName;
  233. end;
  234. end;
  235. procedure TIEPathComboBox.SetShowSpecialFolders(Value: TSpecialFolders);
  236. begin
  237. if ShowSpecialFolders <> Value then
  238. begin
  239. FShowSpecialFolders := Value;
  240. ResetItems;
  241. end;
  242. end;
  243. function TIEPathComboBox.GetDriveIndex(Drive: TDrive; Closest: Boolean): Integer;
  244. var
  245. Index: Integer;
  246. begin
  247. Result := -1;
  248. Drive := UpCase(Drive);
  249. for Index := Items.Count - 1 downto SpecialItems do
  250. begin
  251. if Items[Index][1] = Drive then
  252. begin
  253. Result := Index;
  254. Break;
  255. end
  256. else
  257. if Closest and (Items[Index][1] > Drive) then
  258. begin
  259. Result := Index;
  260. end;
  261. end;
  262. end; {TIEPathComboBox.GetDriveIndex}
  263. function TIEPathComboBox.GetItemImage(Index: Integer): Integer;
  264. var
  265. SpecialFolder: TSpecialFolder;
  266. begin
  267. if Index < SpecialItems then
  268. begin
  269. SpecialFolder := GetItemSpecialFolder(Index);
  270. LoadFolderInfo(FSpecialFolders[SpecialFolder]);
  271. Result := FSpecialFolders[SpecialFolder].ImageIndex;
  272. end
  273. else
  274. begin
  275. Result := DriveInfo.GetImageIndex(ItemDrive[Index])
  276. end;
  277. end;
  278. function TIEPathComboBox.GetItemTextEx(Index: Integer; ForList: Boolean): string;
  279. var
  280. ADrive: TDrive;
  281. SpecialFolder: TSpecialFolder;
  282. begin
  283. if Index < SpecialItems then
  284. begin
  285. SpecialFolder := GetItemSpecialFolder(Index);
  286. LoadFolderInfo(FSpecialFolders[SpecialFolder]);
  287. Result := FSpecialFolders[SpecialFolder].Text;
  288. end
  289. else
  290. begin
  291. ADrive := ItemDrive[Index];
  292. case DisplayStyle of
  293. doPrettyName: Result := DriveInfo.GetPrettyName(ADrive);
  294. doDisplayName: Result := DriveInfo.GetDisplayName(ADrive);
  295. doLongPrettyName: Result := DriveInfo.GetLongPrettyName(ADrive);
  296. end;
  297. end;
  298. end;
  299. procedure TIEPathComboBox.KeyDown(var Key: Word; Shift: TShiftState);
  300. var
  301. Index: Integer;
  302. begin
  303. if DroppedDown and
  304. (Key in [Word('A')..Word('Z'), Word('a')..Word('z')]) then
  305. begin
  306. Index := GetDriveIndex(Char(Key), False);
  307. if Index >= 0 then
  308. begin
  309. DroppedDown := False;
  310. ItemIndex := Index;
  311. if not FDontNotifyPathChange then PathChanged;
  312. end
  313. else Beep;
  314. end
  315. else inherited;
  316. end; { KeyDown }
  317. procedure TIEPathComboBox.ResetItems;
  318. var
  319. Drive: TDrive;
  320. Index: Integer;
  321. begin
  322. if Items.Count > 0 then Items.Clear;
  323. for Index := 0 to SpecialItems - 1 do Items.Add('');
  324. for Drive := FirstDrive to LastDrive do
  325. begin
  326. if DriveInfo[Drive].Valid and
  327. (TDriveType(DriveInfo[Drive].DriveType) in FDriveTypes) then
  328. Items.Add(Drive {+ GetDisplayName(Drive)});
  329. end;
  330. Index := GetDriveIndex(FDrive, False);
  331. if Index >= 0 then
  332. begin
  333. ItemIndex := Index;
  334. end
  335. else
  336. if Items.Count > 0 then
  337. begin
  338. Index := GetDriveIndex(FDrive, True);
  339. if Index < 0 then
  340. begin
  341. Index := Items.Count - 1;
  342. end;
  343. ItemIndex := Index;
  344. PathChanged;
  345. end;
  346. end; {TIEPathComboBox.ResetItems }
  347. procedure TIEPathComboBox.SetDrive(Value: TDrive);
  348. var
  349. NewIndex: Integer;
  350. begin
  351. Value := Upcase(Value);
  352. if Value <> FDrive then
  353. begin
  354. DriveInfo.ReadDriveStatus(Value, dsValid);
  355. if DriveInfo[Drive].Valid and
  356. (TDriveType(DriveInfo[Drive].DriveType) in FDriveTypes) then
  357. begin
  358. FDrive := Value;
  359. NewIndex := GetDriveIndex(Drive, False);
  360. if NewIndex >= 0 then
  361. begin
  362. ItemIndex := NewIndex;
  363. if not FDontNotifyPathChange then PathChanged;
  364. end;
  365. end;
  366. end;
  367. end; {TIEPathComboBox.SetDrive}
  368. procedure TIEPathComboBox.SetDriveTypes(Value: TDriveTypes);
  369. begin
  370. if FDriveTypes <> Value then
  371. begin
  372. FDriveTypes := DriveTypes;
  373. ResetItems;
  374. end;
  375. end; {TIEPathComboBox.SetDriveTypes}
  376. procedure TIEPathComboBox.SetDisplayStyle(Value: TVolumeDisplayStyle);
  377. begin
  378. if FDisplayStyle <> Value then
  379. begin
  380. FDisplayStyle := Value;
  381. ResetItems;
  382. Invalidate;
  383. end;
  384. end; {TIEPathComboBox.SetDisplayStyle}
  385. procedure TIEPathComboBox.PathChanged;
  386. var
  387. Index: Integer;
  388. SpecialFolder: TSpecialFolder;
  389. begin
  390. if ItemIndex < SpecialItems then
  391. begin
  392. SpecialFolder := GetItemSpecialFolder(Index);
  393. FPath := FSpecialFolders[SpecialFolder].Path;
  394. FDrive := Upcase(FPath[1]);
  395. Index := GetDriveIndex(FDrive, False);
  396. if Index >= 0 then
  397. begin
  398. ItemIndex := Index;
  399. end
  400. end
  401. else
  402. begin
  403. FDrive := FocusedDrive;
  404. if DirectoryToSelect = dsRoot then FPath := Format('%s:', [FDrive])
  405. else
  406. begin
  407. GetDir(Integer(FDrive) - Integer('A') + 1, FPath);
  408. FPath := ExcludeTrailingPathDelimiter(FPath);
  409. end;
  410. end;
  411. inherited;
  412. end;
  413. function TIEPathComboBox.GetFocusedDrive: Char;
  414. begin
  415. Result := Upcase(ItemDrive[ItemIndex]);
  416. end; { GetFocusedDrive }
  417. function TIEPathComboBox.GetItemDrive(Index: Integer): TDrive;
  418. begin
  419. Result := UpCase(Items[Index][1]);
  420. end;
  421. procedure TIEPathComboBox.SetPath(Value: string);
  422. var
  423. Expanded: string;
  424. begin
  425. if Value <> '' then
  426. begin
  427. Value := IncludeTrailingPathDelimiter(Value);
  428. if Value <> FPath then
  429. begin
  430. FPath := Value;
  431. Expanded := ExpandFileName(Value);
  432. if Pos(':', Expanded) <> 2 then
  433. raise Exception.CreateFmt('"%s" is not valid local path.', [Value]);
  434. FDontNotifyPathChange := True;
  435. try
  436. Drive := Expanded[1];
  437. finally
  438. FDontNotifyPathChange := False;
  439. end;
  440. end;
  441. end;
  442. end; { SetPath }
  443. procedure Register;
  444. begin
  445. RegisterComponents('DriveDir', [TIEPathComboBox]);
  446. end;
  447. end.