CustomDriveView.pas 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319
  1. unit CustomDriveView;
  2. interface
  3. uses
  4. Classes, ComCtrls, CommCtrl, Windows, Controls, Forms, ShlObj, Messages,
  5. Graphics,
  6. DragDrop, CustomDirView, IEDriveInfo, DragDropFilesEx, PasTools;
  7. type
  8. {Types uses by the function IterateSubTree:}
  9. {TRecursiveScan: determines, wich nodes are scanned by the function IterateSubTree:
  10. rsNoRecursive: Scan startnode only.
  11. rsRecursiveExisting: Scan all subnodes of the startnode but not new created subnodes.}
  12. TRecursiveScan = (rsNoRecursive, rsRecursiveExisting);
  13. {TScanStartnode: determines, wether the startnode should also be scanned:}
  14. TScanStartNode = (coNoScanStartNode, coScanStartNode);
  15. TCallBackFunc = function(var Node: TTreeNode; Data: Pointer): Boolean of object;
  16. type
  17. TCustomDriveView = class(TCustomTreeView)
  18. protected
  19. FParentForm: TCustomForm;
  20. FDragFileList: TStringList;
  21. FDragDropFilesEx: TCustomizableDragDropFilesEx;
  22. FDragImageList: TDragImageList;
  23. FDragDrive: string;
  24. FExeDrag: Boolean;
  25. FDDLinkOnExeDrag: Boolean;
  26. FDragNode: TTreeNode;
  27. FDragStartTime: FILETIME;
  28. FDragPos: TPoint;
  29. FStartPos: TPoint;
  30. FContextMenu: Boolean;
  31. FCanChange: Boolean;
  32. FUseSystemContextMenu: Boolean;
  33. FDimmHiddenDirs: Boolean;
  34. FShowHiddenDirs: Boolean;
  35. FNaturalOrderNumericalSorting: Boolean;
  36. FDarkMode: Boolean;
  37. FContinue: Boolean;
  38. FImageList: TImageList;
  39. FScrollOnDragOver: TTreeViewScrollOnDragOver;
  40. FRecreatingHandle: Boolean;
  41. FOnDDDragEnter: TDDOnDragEnter;
  42. FOnDDDragLeave: TDDOnDragLeave;
  43. FOnDDDragOver: TDDOnDragOver;
  44. FOnDDDrop: TDDOnDrop;
  45. FOnDDQueryContinueDrag: TDDOnQueryContinueDrag;
  46. FOnDDChooseEffect: TDDOnChooseEffect;
  47. FOnDDGiveFeedback: TDDOnGiveFeedback;
  48. FOnDDDragDetect: TDDOnDragDetect;
  49. FOnDDProcessDropped: TOnProcessDropped;
  50. FOnDDError: TDDErrorEvent;
  51. FOnDDExecuted: TDDExecutedEvent;
  52. FOnDDFileOperation: TDDFileOperationEvent;
  53. FOnDDFileOperationExecuted: TDDFileOperationExecutedEvent;
  54. FOnDDCreateDragFileList: TDDOnCreateDragFileList;
  55. FOnDDEnd: TNotifyEvent;
  56. FOnDDCreateDataObject: TDDOnCreateDataObject;
  57. FLastDDResult: TDragResult;
  58. FOnBusy: TDirViewBusy;
  59. function GetTargetPopupMenu: Boolean;
  60. procedure SetTargetPopUpMenu(Value: Boolean);
  61. procedure SetDimmHiddenDirs(Value: Boolean);
  62. procedure SetShowHiddenDirs(Value: Boolean);
  63. procedure SetNaturalOrderNumericalSorting(Value: Boolean);
  64. procedure SetDarkMode(Value: Boolean);
  65. function GetDirectory: string; virtual;
  66. procedure SetDirectory(Value: string); virtual;
  67. function GetCustomDirView: TCustomDirView; virtual; abstract;
  68. procedure SetCustomDirView(Value: TCustomDirView); virtual; abstract;
  69. procedure CreateWnd; override;
  70. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  71. function GetNodeFromHItem(Item: TTVItem): TTreeNode;
  72. function IsCustomDrawn(Target: TCustomDrawTarget; Stage: TCustomDrawStage): Boolean; override;
  73. function CustomDrawItem(Node: TTreeNode; State: TCustomDrawState;
  74. Stage: TCustomDrawStage; var PaintImages: Boolean): Boolean; override;
  75. procedure NeedImageLists(Recreate: Boolean);
  76. procedure DoCompare(Sender: TObject; Node1, Node2: TTreeNode; Data: Integer; var Compare: Integer);
  77. function DoCompareText(Text1, Text2: string): Integer;
  78. procedure UpdateItemHeight;
  79. procedure CNNotify(var Msg: TWMNotify); message CN_NOTIFY;
  80. procedure CMColorChanged(var Msg: TMessage); message CM_COLORCHANGED;
  81. procedure CMRecreateWnd(var Msg: TMessage); message CM_RECREATEWND;
  82. procedure WMLButtonDown(var Msg: TWMLButtonDown); message WM_LBUTTONDOWN;
  83. procedure WMLButtonUp(var Msg: TWMLButtonDown); message WM_LBUTTONUP;
  84. procedure WMRButtonDown(var Msg: TWMRButtonDown); message WM_RBUTTONDOWN;
  85. procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
  86. procedure WMContextMenu(var Msg: TWMContextMenu); message WM_CONTEXTMENU;
  87. procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
  88. procedure CMDPIChanged(var Message: TMessage); message CM_DPICHANGED;
  89. procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  90. procedure Delete(Node: TTreeNode); override;
  91. procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  92. procedure KeyPress(var Key: Char); override;
  93. procedure KeyUp(var Key: Word; Shift: TShiftState); override;
  94. procedure InternalOnDrawItem(Sender: TCustomTreeView; Node: TTreeNode;
  95. State: TCustomDrawState; var DefaultDraw: Boolean);
  96. procedure DDDragEnter(DataObj: IDataObject; KeyState: Longint;
  97. Point: TPoint; var Effect: Longint; var Accept: Boolean);
  98. procedure DDDragLeave;
  99. procedure DDDragOver(KeyState: Longint; Point: TPoint; var Effect: Longint; PreferredEffect: LongInt);
  100. procedure DDDrop(DataObj: IDataObject; KeyState: Longint; Point: TPoint;
  101. var Effect: Longint);
  102. procedure DDQueryContinueDrag(EscapePressed: BOOL; KeyState: Longint;
  103. var Result: HResult);
  104. procedure DDDropHandlerSucceeded(Sender: TObject; KeyState: Longint;
  105. Point: TPoint; Effect: Longint);
  106. procedure DDGiveFeedback(Effect: Longint; var Result: HResult);
  107. procedure DDProcessDropped(Sender: TObject; KeyState: Longint;
  108. Point: TPoint; Effect: Longint);
  109. procedure DDError(Error: TDDError); virtual;
  110. procedure DDSpecifyDropTarget(Sender: TObject; DragDropHandler: Boolean;
  111. Point: TPoint; var PIDL: PItemIDList; var Filename: string);
  112. procedure DDDragDetect(KeyState: Longint; DetectStart, Point: TPoint;
  113. DragStatus: TDragDetectStatus); virtual;
  114. procedure PerformDragDropFileOperation(Node: TTreeNode; Effect: Integer); virtual; abstract;
  115. procedure DDChooseEffect(KeyState: Integer; var Effect: Integer; PreferredEffect: Integer); virtual;
  116. function DragCompleteFileList: Boolean; virtual; abstract;
  117. function DDExecute: TDragResult; virtual;
  118. function DDSourceEffects: TDropEffectSet; virtual; abstract;
  119. function NodePath(Node: TTreeNode): string; virtual; abstract;
  120. function NodeIsRecycleBin(Node: TTreeNode): Boolean; virtual;
  121. function NodePathExists(Node: TTreeNode): Boolean; virtual;
  122. function NodeColor(Node: TTreeNode): TColor; virtual; abstract;
  123. function NodeCanDrag(Node: TTreeNode): Boolean; virtual;
  124. function NodeOverlayIndexes(Node: TTreeNode): Word; virtual;
  125. function FindPathNode(Path: string): TTreeNode; virtual; abstract;
  126. procedure ClearDragFileList(FileList: TFileList); virtual;
  127. procedure AddToDragFileList(FileList: TFileList; Node: TTreeNode); virtual;
  128. procedure ValidateDirectoryEx(Node: TTreeNode; Recurse: TRecursiveScan;
  129. NewDirs: Boolean); virtual; abstract;
  130. procedure RebuildTree; virtual; abstract;
  131. procedure DisplayContextMenu(Node: TTreeNode; ScreenPos: TPoint); virtual; abstract;
  132. procedure DisplayPropertiesMenu(Node: TTreeNode); virtual; abstract;
  133. procedure ScrollOnDragOverBeforeUpdate(ObjectToValidate: TObject);
  134. procedure ScrollOnDragOverAfterUpdate;
  135. function DoBusy(Busy: Integer): Boolean;
  136. function StartBusy: Boolean;
  137. procedure EndBusy;
  138. function IsBusy: Boolean;
  139. property ImageList: TImageList read FImageList;
  140. public
  141. constructor Create(AOwner: TComponent); override;
  142. destructor Destroy; override;
  143. procedure ValidateDirectory(Node: TTreeNode);
  144. procedure CenterNode(Node: TTreeNode); virtual;
  145. function SortChildren(ParentNode: TTreeNode; Recurse: Boolean): Boolean;
  146. function IterateSubTree(var StartNode : TTreeNode;
  147. CallBackFunc: TCallBackFunc; Recurse: TRecursiveScan;
  148. ScanStartNode: TScanStartNode; Data: Pointer): Boolean;
  149. function NodePathName(Node: TTreeNode): string; virtual; abstract;
  150. property DragDropFilesEx: TCustomizableDragDropFilesEx read FDragDropFilesEx;
  151. property UseSystemContextMenu: Boolean read FUseSystemContextMenu
  152. write FUseSystemContextMenu default True;
  153. property DimmHiddenDirs: Boolean read FDimmHiddenDirs
  154. write SetDimmHiddenDirs default False;
  155. property ShowHiddenDirs: Boolean read FShowHiddenDirs
  156. write SetShowHiddenDirs default False;
  157. property NaturalOrderNumericalSorting: Boolean read FNaturalOrderNumericalSorting write SetNaturalOrderNumericalSorting;
  158. property DarkMode: Boolean read FDarkMode write SetDarkMode;
  159. property DDLinkOnExeDrag: Boolean read FDDLinkOnExeDrag write FDDLinkOnExeDrag default True;
  160. {The mouse has entered the component window as a target of a drag&drop operation:}
  161. property OnDDDragEnter: TDDOnDragEnter read FOnDDDragEnter write FOnDDDragEnter;
  162. {The mouse has leaved the component window as a target of a drag&drop operation:}
  163. property OnDDDragLeave: TDDOnDragLeave read FOnDDDragLeave write FOnDDDragLeave;
  164. {The mouse is dragging in the component window as a target of a drag&drop operation:}
  165. property OnDDDragOver: TDDOnDragOver read FOnDDDragOver write FOnDDDragOver;
  166. {The Drag&drop operation is about to be executed:}
  167. property OnDDDrop: TDDOnDrop read FOnDDDrop write FOnDDDrop;
  168. property OnDDQueryContinueDrag: TDDOnQueryContinueDrag read FOnDDQueryContinueDrag write FOnDDQueryContinueDrag;
  169. property OnDDChooseEffect: TDDOnChooseEffect read FOnDDChooseEffect write FOnDDChooseEffect;
  170. property OnDDGiveFeedback: TDDOnGiveFeedback read FOnDDGiveFeedback write FOnDDGiveFeedback;
  171. {A drag&drop operation is about to be initiated whith the components window as the source:}
  172. property OnDDDragDetect: TDDOnDragDetect read FOnDDDragDetect write FOnDDDragDetect;
  173. {The component window is the target of a drag&drop operation:}
  174. property OnDDProcessDropped: TOnProcessDropped read FOnDDProcessDropped write FOnDDProcessDropped;
  175. {An error has occurred during a drag&drop operation:}
  176. property OnDDError: TDDErrorEvent read FOnDDError write FOnDDError;
  177. {The drag&drop operation has been executed:}
  178. property OnDDExecuted: TDDExecutedEvent read FOnDDExecuted write FOnDDExecuted;
  179. {Event is fired just before executing the fileoperation. This event is also fired when
  180. files are pasted from the clipboard:}
  181. property OnDDFileOperation: TDDFileOperationEvent read FOnDDFileOperation write FOnDDFileOperation;
  182. {Event is fired after executing the fileoperation. This event is also fired when
  183. files are pasted from the clipboard:}
  184. property OnDDFileOperationExecuted: TDDFileOperationExecutedEvent read FOnDDFileOperationExecuted write FOnDDFileOperationExecuted;
  185. property OnDDCreateDragFileList: TDDOnCreateDragFileList
  186. read FOnDDCreateDragFileList write FOnDDCreateDragFileList;
  187. property OnDDEnd: TNotifyEvent
  188. read FOnDDEnd write FOnDDEnd;
  189. property OnDDCreateDataObject: TDDOnCreateDataObject
  190. read FOnDDCreateDataObject write FOnDDCreateDataObject;
  191. property OnBusy: TDirViewBusy read FOnBusy write FOnBusy;
  192. { Show popupmenu when dropping a file with the right mouse button }
  193. property TargetPopUpMenu: Boolean read GetTargetPopUpMenu write SetTargetPopUpMenu default True;
  194. {Current selected directory:}
  195. property Directory: string read GetDirectory write SetDirectory;
  196. property DragNode: TTreeNode read FDragNode;
  197. property Continue: Boolean read FContinue write FContinue;
  198. property LastDDResult: TDragResult read FLastDDResult;
  199. end;
  200. implementation
  201. uses
  202. SysUtils, ShellApi, ImgList, ActiveX, Math,
  203. IEListView, BaseUtils;
  204. constructor TCustomDriveView.Create(AOwner: TComponent);
  205. begin
  206. inherited;
  207. DragMode := dmAutomatic;
  208. FDragFileList := TStringList.Create;
  209. FDragDrive := '';
  210. FExeDrag := False;
  211. FDDLinkOnExeDrag := True;
  212. FContextMenu := False;
  213. FCanChange := True;
  214. FUseSystemContextMenu := True;
  215. FContinue := True;
  216. FNaturalOrderNumericalSorting := True;
  217. FDarkMode := False;
  218. FRecreatingHandle := False;
  219. OnCompare := DoCompare;
  220. FDragDropFilesEx := TCustomizableDragDropFilesEx.Create(Self);
  221. with FDragDropFilesEx do
  222. begin
  223. AcceptOwnDnd := True;
  224. {MP}
  225. AutoDetectDnD := False;
  226. {/MP}
  227. BringToFront := True;
  228. CompleteFileList := True;
  229. NeedValid := [nvFileName];
  230. RenderDataOn := rdoEnterAndDropSync;
  231. TargetPopUpMenu := True;
  232. OnDragEnter := DDDragEnter;
  233. OnDragLeave := DDDragLeave;
  234. OnDragOver := DDDragOver;
  235. OnDrop := DDDrop;
  236. OnQueryContinueDrag := DDQueryContinueDrag;
  237. OnSpecifyDropTarget := DDSpecifyDropTarget;
  238. OnDropHandlerSucceeded := DDDropHandlerSucceeded;
  239. OnGiveFeedback := DDGiveFeedback;
  240. OnProcessDropped := DDProcessDropped;
  241. OnDragDetect := DDDragDetect;
  242. end;
  243. OnCustomDrawItem := InternalOnDrawItem;
  244. FScrollOnDragOver := TTreeViewScrollOnDragOver.Create(Self, False);
  245. FScrollOnDragOver.OnBeforeUpdate := ScrollOnDragOverBeforeUpdate;
  246. FScrollOnDragOver.OnAfterUpdate := ScrollOnDragOverAfterUpdate;
  247. end;
  248. destructor TCustomDriveView.Destroy;
  249. begin
  250. FreeAndNil(FScrollOnDragOver);
  251. FreeAndNil(FImageList);
  252. if Assigned(Images) then
  253. Images.Free;
  254. if Assigned(FDragImageList) then
  255. begin
  256. if GlobalDragImageList = FDragImageList then
  257. GlobalDragImageList := nil;
  258. FDragImageList.Free;
  259. end;
  260. FDragFileList.Destroy;
  261. if Assigned(FDragDropFilesEx) then
  262. FDragDropFilesEx.Free;
  263. inherited Destroy;
  264. end;
  265. procedure TCustomDriveView.UpdateItemHeight;
  266. var
  267. ImageHeight: Integer;
  268. TextHeight: Integer;
  269. begin
  270. ImageHeight := (Images.Width * 9) div 8;
  271. // 16 seems to be the system default tree view item height
  272. TextHeight := ScaleByControlTextHeightRunTime(Canvas, 16);
  273. TreeView_SetItemHeight(Handle, Max(ImageHeight, TextHeight));
  274. end;
  275. procedure TCustomDriveView.CMFontChanged(var Message: TMessage);
  276. begin
  277. inherited;
  278. UpdateItemHeight;
  279. end;
  280. procedure TCustomDriveView.NeedImageLists(Recreate: Boolean);
  281. var
  282. AImages: TImageList;
  283. begin
  284. if not Assigned(Images) then
  285. begin
  286. Images := TImageList.Create(Self);
  287. Images.BkColor := Color;
  288. end;
  289. AImages := ShellImageListForControl(Self, ilsSmall);
  290. if Images.Handle <> AImages.Handle then
  291. begin
  292. Images.Handle := AImages.Handle;
  293. end;
  294. if (not Assigned(FImageList)) or Recreate then
  295. begin
  296. if Assigned(FImageList) then
  297. FImageList.Free;
  298. FImageList := OverlayImageList(Images.Width);
  299. end;
  300. UpdateItemHeight;
  301. end;
  302. procedure TCustomDriveView.CMDPIChanged(var Message: TMessage);
  303. begin
  304. inherited;
  305. NeedImageLists(True);
  306. end;
  307. procedure TCustomDriveView.CreateWnd;
  308. begin
  309. inherited;
  310. if DarkMode then AllowDarkModeForWindow(Self, DarkMode);
  311. NeedImageLists(False);
  312. if not (csDesigning in ComponentState) then
  313. FDragImageList := TDragImageList.Create(Self);
  314. if not Assigned(GlobalDragImageList) then
  315. GlobalDragImageList := FDragImageList;
  316. FDragDropFilesEx.DragDropControl := Self;
  317. FParentForm := GetParentForm(Self);
  318. end;
  319. procedure TCustomDriveView.Notification(AComponent: TComponent; Operation: TOperation);
  320. begin
  321. inherited;
  322. if Operation = opRemove then
  323. begin
  324. if AComponent = GetCustomDirView then SetCustomDirView(nil);
  325. end;
  326. end;
  327. procedure TCustomDriveView.InternalOnDrawItem(Sender: TCustomTreeView; Node: TTreeNode;
  328. State: TCustomDrawState; var DefaultDraw: Boolean);
  329. var
  330. FItemColor: TColor;
  331. begin
  332. if Assigned(Node) and Assigned(Node.Data) and (Node <> DropTarget) then
  333. begin
  334. if not Node.Selected then
  335. begin
  336. FItemColor := NodeColor(Node);
  337. if (FItemColor <> clDefaultItemColor) and
  338. (Canvas.Font.Color <> FItemColor) then
  339. Canvas.Font.Color := FItemColor;
  340. end
  341. else
  342. if (not Self.Focused) and HideSelection then
  343. begin
  344. Canvas.Brush.Color := clBtnFace;
  345. Canvas.Font.Color := clBtnText;
  346. end
  347. else
  348. if Node.Selected and (not Self.Focused) and DarkMode then
  349. begin
  350. Canvas.Font.Color := Font.Color;
  351. end;
  352. end;
  353. end; {InternalOnDrawItem}
  354. procedure TCustomDriveView.ScrollOnDragOverBeforeUpdate(ObjectToValidate: TObject);
  355. var
  356. NodeToValidate: TTreeNode;
  357. begin
  358. GlobalDragImageList.HideDragImage;
  359. if Assigned(ObjectToValidate) then
  360. begin
  361. NodeToValidate := (ObjectToValidate as TTreeNode);
  362. if not NodeToValidate.HasChildren then
  363. ValidateDirectory(NodeToValidate);
  364. end;
  365. end;
  366. procedure TCustomDriveView.ScrollOnDragOverAfterUpdate;
  367. begin
  368. GlobalDragImageList.ShowDragImage;
  369. end;
  370. procedure TCustomDriveView.DDDragEnter(DataObj: IDataObject; KeyState: Longint;
  371. Point: TPoint; var Effect: Longint; var Accept: Boolean);
  372. var
  373. Index: Integer;
  374. begin
  375. if (FDragDropFilesEx.FileList.Count > 0) and
  376. (Length(TFDDListItem(FDragDropFilesEx.FileList[0]^).Name) > 0) Then
  377. begin
  378. try
  379. FDragDrive := DriveInfo.GetDriveKey(TFDDListItem(FDragDropFilesEx.FileList[0]^).Name);
  380. except
  381. // WinRAR gives us only filename on "enter", we get a full path only on "drop".
  382. FDragDrive := '';
  383. end;
  384. FExeDrag := FDDLinkOnExeDrag and
  385. (deLink in DragDropFilesEx.TargetEffects) and
  386. ((DragDropFilesEx.AvailableDropEffects and DROPEFFECT_LINK) <> 0);
  387. if FExeDrag then
  388. begin
  389. for Index := 0 to FDragDropFilesEx.FileList.Count - 1 do
  390. if not IsExecutable(TFDDListItem(FDragDropFilesEx.FileList[Index]^).Name) then
  391. begin
  392. FExeDrag := False;
  393. Break;
  394. end;
  395. end;
  396. end
  397. else
  398. begin
  399. FDragDrive := '';
  400. end;
  401. FScrollOnDragOver.StartDrag;
  402. if Assigned(FOnDDDragEnter) then
  403. FOnDDDragEnter(Self, DataObj, KeyState, Point, Effect, Accept);
  404. end; {DDDragEnter}
  405. procedure TCustomDriveView.DDDragLeave;
  406. begin
  407. if Assigned(DropTarget) then
  408. begin
  409. if GlobalDragImageList.Dragging then
  410. GlobalDragImageList.HideDragImage;
  411. DropTarget := nil;
  412. Update;
  413. end;
  414. if Assigned(FOnDDDragLeave) then
  415. FOnDDDragLeave(Self);
  416. end; {DragLeave}
  417. procedure TCustomDriveView.DDDragOver(KeyState: Longint; Point: TPoint; var Effect: Longint; PreferredEffect: Longint);
  418. var
  419. Node: TTreeNode;
  420. Rect1: TRect;
  421. UpdateImage: Boolean;
  422. LastDragNode: TTreeNode;
  423. begin
  424. if Effect <> DROPEFFECT_NONE then
  425. begin
  426. Node := GetNodeAt(Point.X, Point.Y);
  427. if Assigned(Node) then
  428. begin
  429. LastDragNode := DropTarget;
  430. UpdateImage := False;
  431. if GlobalDragImageList.Dragging and (LastDragNode <> Node) then
  432. begin
  433. if Assigned(LastDragNode) then
  434. begin
  435. Rect1 := LastDragNode.DisplayRect(True);
  436. if Rect1.Right >= Point.x - GlobalDragImageList.GetHotSpot.X then
  437. begin
  438. GlobalDragImageList.HideDragImage;
  439. UpdateImage := True;
  440. end
  441. else
  442. begin
  443. Rect1 := Node.DisplayRect(True);
  444. if Rect1.Right >= Point.x - GlobalDragImageList.GetHotSpot.X then
  445. begin
  446. GlobalDragImageList.HideDragImage;
  447. UpdateImage := True;
  448. end
  449. end;
  450. end
  451. else
  452. begin
  453. {LastDragNode not assigned:}
  454. GlobalDragImageList.HideDragImage;
  455. UpdateImage := True;
  456. end;
  457. end;
  458. DropTarget := Node;
  459. if UpdateImage then
  460. GlobalDragImageList.ShowDragImage;
  461. {Drop-operation allowed at this location?}
  462. if Assigned(FDragNode) and
  463. (Effect <> DROPEFFECT_LINK) and
  464. ((Node = FDragNode) or Node.HasAsParent(FDragNode) or (FDragNode.Parent = Node)) then
  465. Effect := DROPEFFECT_NONE;
  466. FScrollOnDragOver.DragOver(Point);
  467. end {Assigned(Node)}
  468. else
  469. begin
  470. DropTarget := nil;
  471. end;
  472. end;
  473. DDChooseEffect(KeyState, Effect, PreferredEffect);
  474. if Assigned(FOnDDDragOver) then
  475. FOnDDDragOver(Self, KeyState, Point, Effect);
  476. if not Assigned(DropTarget) then Effect := DROPEFFECT_NONE
  477. else
  478. if NodeIsRecycleBin(DropTarget) then
  479. begin
  480. if FDragDropFilesEx.FileNamesAreMapped then Effect := DROPEFFECT_NONE
  481. else Effect := DROPEFFECT_MOVE;
  482. end;
  483. end; {DDDragOver}
  484. procedure TCustomDriveView.DDDrop(DataObj: IDataObject; KeyState: Longint;
  485. Point: TPoint; var Effect: Longint);
  486. begin
  487. if GlobalDragImageList.Dragging then
  488. GlobalDragImageList.HideDragImage;
  489. if Effect = DROPEFFECT_NONE then
  490. DropTarget := nil;
  491. if Assigned(FOnDDDrop) then
  492. FOnDDDrop(Self, DataObj, KeyState, Point, Effect);
  493. end; {DDDrop}
  494. procedure TCustomDriveView.DDQueryContinueDrag(EscapePressed: BOOL; KeyState: Longint;
  495. var Result: HResult);
  496. var
  497. Point: TPoint;
  498. ClientPoint: TPoint;
  499. KnowTime: FILETIME;
  500. begin
  501. if Result = DRAGDROP_S_DROP then
  502. begin
  503. GetSystemTimeAsFileTime(KnowTime);
  504. if ((Int64(KnowTime) - Int64(FDragStartTime)) <= DDDragStartDelay) then
  505. Result := DRAGDROP_S_CANCEL;
  506. end;
  507. if Assigned(FOnDDQueryContinueDrag) then
  508. FOnDDQueryContinueDrag(Self, EscapePressed, KeyState, Result);
  509. if EscapePressed then
  510. begin
  511. if GlobalDragImageList.Dragging then
  512. GlobalDragImageList.HideDragImage;
  513. DropTarget := nil;
  514. end
  515. else
  516. begin
  517. if GlobalDragImageList.Dragging then
  518. begin
  519. GetCursorPos(Point);
  520. {Convert screen coordinates to the parentforms coordinates:}
  521. ClientPoint := FParentForm.ScreenToClient(Point);
  522. {Move the drag image to the new position and show it:}
  523. if not CompareMem(@ClientPoint, @FDragPos, SizeOf(TPoint)) then
  524. begin
  525. FDragPos := ClientPoint;
  526. if PtInRect(FParentForm.BoundsRect, Point) then
  527. begin
  528. GlobalDragImageList.DragMove(ClientPoint.X, ClientPoint.Y);
  529. GlobalDragImageList.ShowDragImage;
  530. end
  531. else GlobalDragImageList.HideDragImage;
  532. end;
  533. end;
  534. end;
  535. end; {DDQueryContinueDrag}
  536. procedure TCustomDriveView.DDDropHandlerSucceeded(Sender: TObject;
  537. KeyState: Integer; Point: TPoint; Effect: Integer);
  538. begin
  539. DropTarget := nil;
  540. end;
  541. procedure TCustomDriveView.DDChooseEffect(KeyState: Integer; var Effect: Integer; PreferredEffect: Integer);
  542. begin
  543. if Assigned(FOnDDChooseEffect) then
  544. FOnDDChooseEffect(Self, KeyState, Effect);
  545. end;
  546. procedure TCustomDriveView.DDGiveFeedback(Effect: Longint; var Result: HResult);
  547. begin
  548. if Assigned(FOnDDGiveFeedback) then
  549. FOnDDGiveFeedback(Self, Effect, Result);
  550. end; {DDGiveFeedback}
  551. procedure TCustomDriveView.DDProcessDropped(Sender: TObject; KeyState: Longint;
  552. Point: TPoint; Effect: Longint);
  553. begin
  554. try
  555. if Assigned(DropTarget) then
  556. try
  557. if NodePathExists(DropTarget) then
  558. begin
  559. if Assigned(FOnDDProcessDropped) then
  560. FOnDDProcessDropped(Self, KeyState, Point, Effect);
  561. if Effect <> DROPEFFECT_NONE then
  562. begin
  563. PerformDragDropFileOperation(DropTarget, Effect);
  564. if Assigned(FOnDDExecuted) then
  565. FOnDDExecuted(Self, Effect);
  566. end;
  567. end
  568. else
  569. begin
  570. ValidateDirectory(DropTarget);
  571. DDError(DDPathNotFoundError);
  572. end;
  573. finally
  574. DropTarget := nil;
  575. ClearDragFileList(FDragDropFilesEx.FileList);
  576. end;
  577. except
  578. Application.HandleException(Self);
  579. end;
  580. end; {ProcessDropped}
  581. procedure TCustomDriveView.DDError(Error: TDDError);
  582. begin
  583. if Assigned(FOnDDError) then FOnDDError(Self, Error)
  584. else raise Exception.CreateFmt(SDragDropError, [Ord(Error)]);
  585. end; {DDError}
  586. procedure TCustomDriveView.DDSpecifyDropTarget(Sender: TObject;
  587. DragDropHandler: Boolean; Point: TPoint; var PIDL: PItemIDList; var Filename: string);
  588. begin
  589. PIDL := nil;
  590. if DragDropHandler and Assigned(DropTarget) then FileName := NodePathName(DropTarget)
  591. else FileName := EmptyStr;
  592. end; {DDSpecifyDropTarget}
  593. procedure TCustomDriveView.DDDragDetect(KeyState: Longint; DetectStart, Point: TPoint;
  594. DragStatus: TDragDetectStatus);
  595. var
  596. P: TPoint;
  597. ImageList: HImageList;
  598. NodeRect: TRect;
  599. FileListCreated: Boolean;
  600. AvoidDragImage: Boolean;
  601. begin
  602. if (DragStatus = ddsDrag) and (not Assigned(FDragNode)) then
  603. begin
  604. P := ScreenToClient(FStartPos);
  605. FDragNode := GetNodeAt(P.X, P.Y);
  606. end;
  607. if Assigned(FOnDDDragDetect) then
  608. FOnDDDragDetect(Self, KeyState, DetectStart, Point, DragStatus);
  609. if (DragStatus = ddsDrag) and Assigned(FDragNode) then
  610. begin
  611. NodeRect := FDragNode.DisplayRect(True);
  612. Dec(NodeRect.Left, 16);
  613. {Check, wether the mouse cursor was within the nodes display rectangle:}
  614. if (NodeRect.Left > P.X) or (NodeRect.Right < P.X) or
  615. (not NodeCanDrag(FDragNode)) then
  616. begin
  617. FDragNode := nil;
  618. Exit;
  619. end;
  620. FDragDrive := '';
  621. ClearDragFileList(FDragDropFilesEx.FileList);
  622. FDragDropFilesEx.CompleteFileList := DragCompleteFileList;
  623. FileListCreated := False;
  624. AvoidDragImage := False;
  625. if Assigned(OnDDCreateDragFileList) then
  626. begin
  627. OnDDCreateDragFileList(Self, FDragDropFilesEx.FileList, FileListCreated);
  628. if FileListCreated then
  629. AvoidDragImage := True;
  630. end;
  631. if not FileListCreated then
  632. begin
  633. AddToDragFileList(FDragDropFilesEx.FileList, FDragNode);
  634. end;
  635. FDragDropFilesEx.SourceEffects := DDSourceEffects;
  636. if FDragDropFilesEx.FileList.Count > 0 then
  637. try
  638. {Create the dragimage:}
  639. GlobalDragImageList := FDragImageList;
  640. if not AvoidDragImage then
  641. begin
  642. {Hide the selection mark to get a proper dragimage:}
  643. if Selected = FDragNode then
  644. Selected := nil;
  645. ImageList := TreeView_CreateDragImage(Handle, FDragNode.ItemID);
  646. {Show the selection mark if it was hidden:}
  647. if not Assigned(Selected) then
  648. Selected := FDragNode;
  649. if ImageList <> Invalid_Handle_Value then
  650. begin
  651. GlobalDragImageList.Handle := ImageList;
  652. GlobalDragImageList.SetDragImage(0, P.X - NodeRect.TopLeft.X, P.Y - NodeRect.TopLeft.Y);
  653. P := FParentForm.ScreenToClient(Point);
  654. GlobalDragImageList.BeginDrag(FParentForm.Handle, P.X, P.Y);
  655. GlobalDragImageList.HideDragImage;
  656. ShowCursor(True);
  657. end;
  658. end;
  659. DropSourceControl := Self;
  660. GetSystemTimeAsFileTime(FDragStartTime);
  661. {Supress the context menu:}
  662. FContextMenu := False;
  663. {Execute the drag&drop-Operation:}
  664. FLastDDResult := DDExecute;
  665. {the drag&drop operation is finished, so clean up the used drag image:}
  666. GlobalDragImageList.EndDrag;
  667. GlobalDragImageList.Clear;
  668. Application.ProcessMessages;
  669. finally
  670. ClearDragFileList(FDragDropFilesEx.FileList);
  671. FDragDrive := '';
  672. DropTarget := nil;
  673. try
  674. if Assigned(OnDDEnd) then
  675. OnDDEnd(Self);
  676. finally
  677. DropSourceControl := nil;
  678. FDragNode := nil;
  679. end;
  680. end;
  681. end;
  682. end; {(DDDragDetect}
  683. function TCustomDriveView.DDExecute: TDragResult;
  684. var
  685. DataObject: TDataObject;
  686. begin
  687. DataObject := nil;
  688. if Assigned(OnDDCreateDataObject) then
  689. OnDDCreateDataObject(Self, DataObject);
  690. Result := FDragDropFilesEx.Execute(DataObject);
  691. end;
  692. function TCustomDriveView.GetNodeFromHItem(Item: TTVItem): TTreeNode;
  693. begin
  694. Result := nil;
  695. if Items <> nil then
  696. with Item do
  697. if (state and TVIF_PARAM) <> 0 then
  698. Result := Pointer(lParam)
  699. else
  700. Result := Items.GetNode(hItem);
  701. end; {GetNodeFromItem}
  702. function TCustomDriveView.IsCustomDrawn(Target: TCustomDrawTarget;
  703. Stage: TCustomDrawStage): Boolean;
  704. begin
  705. Result := inherited IsCustomDrawn(Target, Stage) or
  706. ((Target = dtItem) and (Stage = cdPostPaint));
  707. end;
  708. function TCustomDriveView.CustomDrawItem(Node: TTreeNode; State: TCustomDrawState;
  709. Stage: TCustomDrawStage; var PaintImages: Boolean): Boolean;
  710. var
  711. Point: TPoint;
  712. Index: Integer;
  713. OverlayIndexes: Word;
  714. OverlayIndex: Word;
  715. Image: Word;
  716. begin
  717. Result := inherited CustomDrawItem(Node, State, Stage, PaintImages);
  718. if Result and (Stage = cdPostPaint) then
  719. begin
  720. Assert(Assigned(Node));
  721. OverlayIndexes := NodeOverlayIndexes(Node);
  722. OverlayIndex := 1;
  723. while OverlayIndexes > 0 do
  724. begin
  725. if (OverlayIndex and OverlayIndexes) <> 0 then
  726. begin
  727. Index := 0;
  728. Image := OverlayIndex;
  729. while Image > 1 do
  730. begin
  731. Inc(Index);
  732. Image := Image shr 1;
  733. end;
  734. Point := Node.DisplayRect(True).TopLeft;
  735. Dec(Point.X, Indent);
  736. ImageList_Draw(ImageList.Handle, Index, Self.Canvas.Handle,
  737. Point.X, Point.Y, ILD_TRANSPARENT);
  738. Dec(OverlayIndexes, OverlayIndex);
  739. end;
  740. OverlayIndex := OverlayIndex shl 1;
  741. end;
  742. end;
  743. end;
  744. procedure TCustomDriveView.CNNotify(var Msg: TWMNotify);
  745. begin
  746. case Msg.NMHdr.code of
  747. TVN_BEGINDRAG: DDDragDetect(MK_LBUTTON, FStartPos, Mouse.CursorPos, ddsDrag);
  748. TVN_BEGINRDRAG: DDDragDetect(MK_RBUTTON, FStartPos, Mouse.CursorPos, ddsDrag);
  749. else
  750. inherited;
  751. end;
  752. end; {CNNotify}
  753. procedure TCustomDriveView.CMColorChanged(var Msg: TMessage);
  754. begin
  755. inherited;
  756. if Assigned(Images) then
  757. Images.BkColor := Color;
  758. ForceColorChange(Self);
  759. end;
  760. procedure TCustomDriveView.WMLButtonDown(var Msg: TWMLButtonDown);
  761. begin
  762. if not IsBusy then
  763. begin
  764. FCanChange := False;
  765. GetCursorPos(FStartPos);
  766. inherited;
  767. end;
  768. end; {WMLButtonDown}
  769. procedure TCustomDriveView.WMLButtonUp(var Msg: TWMLButtonDown);
  770. begin
  771. FCanChange := True;
  772. if Assigned(DropTarget) and Assigned(DropTarget.Data) then
  773. Selected := DropTarget;
  774. DropTarget := nil;
  775. inherited;
  776. end; {WMLButtonUp}
  777. procedure TCustomDriveView.WMRButtonDown(var Msg: TWMRButtonDown);
  778. begin
  779. if not IsBusy then
  780. begin
  781. GetCursorPos(FStartPos);
  782. if FDragDropFilesEx.DragDetectStatus <> ddsDrag then
  783. FContextMenu := True;
  784. inherited;
  785. end;
  786. end; {WMRButtonDown}
  787. procedure TCustomDriveView.WMLButtonDblClk(var Message: TWMLButtonDblClk);
  788. begin
  789. if not IsBusy then
  790. begin
  791. inherited;
  792. end;
  793. end;
  794. procedure TCustomDriveView.WMContextMenu(var Msg: TWMContextMenu);
  795. var
  796. Node: TTreeNode;
  797. Point: TPoint;
  798. PrevAutoPopup: Boolean;
  799. begin
  800. PrevAutoPopup := False;
  801. try
  802. if Assigned(PopupMenu) then
  803. begin
  804. PrevAutoPopup := PopupMenu.AutoPopup;
  805. PopupMenu.AutoPopup := False;
  806. end;
  807. inherited;
  808. finally
  809. if Assigned(PopupMenu) then
  810. PopupMenu.AutoPopup := PrevAutoPopup;
  811. end;
  812. FStartPos.X := -1;
  813. FStartPos.Y := -1;
  814. try
  815. if FContextMenu then
  816. begin
  817. Point.X := Msg.XPos;
  818. Point.Y := Msg.YPos;
  819. Point := ScreenToClient(Point);
  820. Node := GetNodeAt(Point.X, Point.Y);
  821. if FUseSystemContextMenu and Assigned(Node) then
  822. begin
  823. if Assigned(OnMouseDown) then
  824. OnMouseDown(Self, mbRight, [], Msg.XPos, Msg.YPos);
  825. DisplayContextMenu(Node, Mouse.CursorPos);
  826. end
  827. else
  828. begin
  829. if Assigned(PopupMenu) then
  830. PopupMenu.Popup(Msg.XPos, Msg.YPos);
  831. end;
  832. end;
  833. FContextMenu := False;
  834. finally
  835. DropTarget := nil;
  836. end;
  837. end; {WMContextMenu}
  838. procedure TCustomDriveView.CMRecreateWnd(var Msg: TMessage);
  839. var
  840. HadHandle: Boolean;
  841. begin
  842. HadHandle := HandleAllocated;
  843. inherited;
  844. // If the control is not showing (e.g. because the machine is locked), the handle is not recreated.
  845. // If contents is reloaded (LoadPath) without handle allocated, it crashes
  846. // (as the handle is implicitly created somewhere in the middle of the reload and chaos ensures).
  847. if HadHandle then
  848. begin
  849. Assert(not FRecreatingHandle);
  850. FRecreatingHandle := True;
  851. try
  852. HandleNeeded;
  853. finally
  854. FRecreatingHandle := False;
  855. end;
  856. end;
  857. end;
  858. procedure TCustomDriveView.Delete(Node: TTreeNode);
  859. begin
  860. if Node = FDragNode then
  861. FDragNode := nil;
  862. if Node = DropTarget then
  863. begin
  864. DropTarget := nil;
  865. Update;
  866. end;
  867. inherited;
  868. end; {OnDelete}
  869. procedure TCustomDriveView.WMKeyDown(var Message: TWMKeyDown);
  870. begin
  871. if not IsBusy then
  872. begin
  873. inherited;
  874. end;
  875. end;
  876. procedure TCustomDriveView.KeyDown(var Key: Word; Shift: TShiftState);
  877. begin
  878. if (Key = VK_RETURN) and (ssAlt in Shift) and (not IsEditing) and
  879. Assigned(Selected) then
  880. begin
  881. DisplayPropertiesMenu(Selected);
  882. Key := 0;
  883. end;
  884. inherited;
  885. end; {KeyDown}
  886. procedure TCustomDriveView.KeyPress(var Key : Char);
  887. begin
  888. if Assigned(Selected) then
  889. begin
  890. if not IsEditing then
  891. begin
  892. case Key of
  893. #13, ' ':
  894. begin
  895. Selected.Expanded := not Selected.Expanded;
  896. Key := #0;
  897. end;
  898. '/':
  899. begin
  900. Selected.Collapse(True);
  901. Selected.MakeVisible;
  902. Key := #0;
  903. end;
  904. '*':
  905. Selected.MakeVisible;
  906. end {Case}
  907. end
  908. end;
  909. inherited;
  910. end; {KeyPress}
  911. procedure TCustomDriveView.KeyUp(var Key: Word; Shift: TShiftState);
  912. var
  913. Point: TPoint;
  914. begin
  915. inherited;
  916. if (Key = VK_APPS) and Assigned(Selected) then
  917. begin
  918. Point := ClientToScreen(Selected.DisplayRect(True).TopLeft);
  919. Inc(Point.Y, 20);
  920. DisplayContextMenu(Selected, Point);
  921. end;
  922. end; {KeyUp}
  923. procedure TCustomDriveView.ValidateDirectory(Node: TTreeNode);
  924. begin
  925. ValidateDirectoryEx(Node, rsRecursiveExisting, False);
  926. end; {ValidateDirectory}
  927. procedure TCustomDriveView.CenterNode(Node: TTreeNode);
  928. var
  929. NodePos: TRect;
  930. ScrollInfo: TScrollInfo;
  931. begin
  932. if Assigned(Node) and (Items.Count > 0) then
  933. begin
  934. Node.MakeVisible;
  935. NodePos := Node.DisplayRect(False);
  936. with ScrollInfo do
  937. begin
  938. cbSize := SizeOf(ScrollInfo);
  939. fMask := SIF_ALL;
  940. nMin := 0;
  941. nMax := 0;
  942. nPage := 0;
  943. end;
  944. GetScrollInfo(Handle, SB_VERT, ScrollInfo);
  945. if ScrollInfo.nMin <> ScrollInfo.nMax then
  946. begin
  947. {Scroll tree up:}
  948. if (NodePos.Top < Height div 4) and (ScrollInfo.nPos > 0) then
  949. begin
  950. ScrollInfo.fMask := SIF_POS;
  951. while (ScrollInfo.nPos > 0) and (NodePos.Top < (Height div 4)) do
  952. begin
  953. Perform(WM_VSCROLL, SB_LINEUP, 0);
  954. GetScrollInfo(Handle, SB_VERT, ScrollInfo);
  955. NodePos := Node.DisplayRect(False);
  956. end;
  957. end
  958. else
  959. if (NodePos.Top > ((Height * 3) div 4)) then
  960. begin
  961. {Scroll tree down:}
  962. ScrollInfo.fMask := SIF_POS;
  963. while (ScrollInfo.nPos + ABS(ScrollInfo.nPage) < ScrollInfo.nMax) and
  964. (NodePos.Top > ((Height * 3) div 4)) and
  965. (ScrollInfo.nPage > 0) do
  966. begin
  967. Perform(WM_VSCROLL, SB_LINEDOWN, 0);
  968. GetScrollInfo(Handle, SB_VERT, ScrollInfo);
  969. NodePos := Node.DisplayRect(False);
  970. end;
  971. end;
  972. NodePos := Node.DisplayRect(True);
  973. end;
  974. if NodePos.Left < 50 then
  975. Perform(WM_HSCROLL, SB_PAGELEFT, 0);
  976. end;
  977. end; {CenterNode}
  978. function TCustomDriveView.DoCompareText(Text1, Text2: string): Integer;
  979. begin
  980. Result := CompareLogicalTextPas(Text1, Text2, NaturalOrderNumericalSorting);
  981. end;
  982. procedure TCustomDriveView.DoCompare(Sender: TObject; Node1, Node2: TTreeNode; Data: Integer; var Compare: Integer);
  983. begin
  984. Compare := DoCompareText(Node1.Text, Node2.Text);
  985. end;
  986. function TCustomDriveView.SortChildren(ParentNode: TTreeNode; Recurse: Boolean): Boolean;
  987. begin
  988. Result := Assigned(ParentNode) and ParentNode.AlphaSort(Recurse);
  989. end; {SortChildren}
  990. function TCustomDriveView.IterateSubTree(var StartNode : TTreeNode;
  991. CallBackFunc: TCallBackFunc; Recurse: TRecursiveScan;
  992. ScanStartNode: TScanStartNode; Data: Pointer): Boolean;
  993. function ScanSubTree(var StartNode: TTreeNode): Boolean;
  994. var
  995. Node: TTreeNode;
  996. NextNode: TTreeNode;
  997. NodeHasChilds: Boolean;
  998. begin
  999. Result := False;
  1000. if not Assigned(StartNode) then Exit;
  1001. Node := StartNode.GetFirstChild;
  1002. while Assigned(Node) and FContinue do
  1003. begin
  1004. NextNode := StartNode.GetNextChild(Node);
  1005. NodeHasChilds := Node.HasChildren;
  1006. if (not FContinue) or (not CallBackFunc(Node, Data)) then Exit;
  1007. if Assigned(Node) and
  1008. (Recurse = rsRecursiveExisting) and NodeHasChilds then
  1009. begin
  1010. if (not ScanSubTree(Node)) or (not FContinue) then Exit;
  1011. end;
  1012. Node := NextNode;
  1013. end;
  1014. Result := True;
  1015. end; {ScanSubTree}
  1016. begin {IterateSubTree}
  1017. Result := False;
  1018. FContinue := True;
  1019. if Assigned(CallBackFunc) then
  1020. begin
  1021. if ScanStartNode = coScanStartNode then
  1022. begin
  1023. CallBackFunc(StartNode, Data);
  1024. end;
  1025. if (not Assigned(StartNode)) or
  1026. FContinue and ScanSubTree(StartNode) then
  1027. begin
  1028. Result := True;
  1029. end;
  1030. end;
  1031. end; {IterateSubTree}
  1032. procedure TCustomDriveView.ClearDragFileList(FileList: TFileList);
  1033. begin
  1034. FileList.Clear;
  1035. end;
  1036. procedure TCustomDriveView.AddToDragFileList(FileList: TFileList; Node: TTreeNode);
  1037. begin
  1038. FileList.AddItem(nil, NodePathName(Node));
  1039. end;
  1040. function TCustomDriveView.NodeCanDrag(Node: TTreeNode): Boolean;
  1041. begin
  1042. Result := True;
  1043. end;
  1044. function TCustomDriveView.NodeOverlayIndexes(Node: TTreeNode): Word;
  1045. begin
  1046. Result := oiNoOverlay;
  1047. end;
  1048. function TCustomDriveView.NodeIsRecycleBin(Node: TTreeNode): Boolean;
  1049. begin
  1050. Result := False;
  1051. end;
  1052. function TCustomDriveView.NodePathExists(Node: TTreeNode): Boolean;
  1053. begin
  1054. Result := True;
  1055. end;
  1056. procedure TCustomDriveView.SetDimmHiddenDirs(Value: Boolean);
  1057. begin
  1058. if Value <> FDimmHiddenDirs then
  1059. begin
  1060. FDimmHiddenDirs := Value;
  1061. Self.Invalidate;
  1062. end;
  1063. end; {SetDimmHiddenDirs}
  1064. procedure TCustomDriveView.SetShowHiddenDirs(Value: Boolean);
  1065. begin
  1066. if Value <> FShowHiddenDirs then
  1067. begin
  1068. FShowHiddenDirs := Value;
  1069. RebuildTree;
  1070. end;
  1071. end; {SetDimmHiddenDirs}
  1072. procedure TCustomDriveView.SetNaturalOrderNumericalSorting(Value: Boolean);
  1073. begin
  1074. if NaturalOrderNumericalSorting <> Value then
  1075. begin
  1076. FNaturalOrderNumericalSorting := Value;
  1077. AlphaSort;
  1078. end;
  1079. end;
  1080. procedure TCustomDriveView.SetDarkMode(Value: Boolean);
  1081. begin
  1082. if DarkMode <> Value then
  1083. begin
  1084. FDarkMode := Value;
  1085. // See TCustomDirView.SetDarkMode
  1086. if HandleAllocated then AllowDarkModeForWindow(Self, DarkMode);
  1087. end;
  1088. end;
  1089. function TCustomDriveView.GetTargetPopupMenu: Boolean;
  1090. begin
  1091. if Assigned(FDragDropFilesEx) then Result := FDragDropFilesEx.TargetPopupMenu
  1092. else Result := True;
  1093. end;
  1094. procedure TCustomDriveView.SetTargetPopUpMenu(Value: Boolean);
  1095. begin
  1096. if Assigned(FDragDropFilesEx) then
  1097. FDragDropFilesEx.TargetPopupMenu := Value;
  1098. end; {SetTargetPopUpMenu}
  1099. function TCustomDriveView.GetDirectory: string;
  1100. begin
  1101. if Assigned(Selected) then Result := NodePathName(Selected)
  1102. else Result := '';
  1103. end; {GetDirectory}
  1104. procedure TCustomDriveView.SetDirectory(Value: string);
  1105. var
  1106. NewSelected: TTreeNode;
  1107. begin
  1108. NewSelected := FindPathNode(Value);
  1109. if Assigned(NewSelected) and (NewSelected <> Selected) then
  1110. begin
  1111. FCanChange := True;
  1112. NewSelected.MakeVisible;
  1113. Selected := NewSelected;
  1114. end
  1115. else
  1116. if csDesigning in ComponentState then
  1117. Selected := nil;
  1118. end; {SetDirectory}
  1119. function TCustomDriveView.DoBusy(Busy: Integer): Boolean;
  1120. begin
  1121. Result := True;
  1122. if Assigned(OnBusy) then
  1123. begin
  1124. OnBusy(Self, Busy, Result);
  1125. end;
  1126. end;
  1127. function TCustomDriveView.StartBusy: Boolean;
  1128. begin
  1129. Result := DoBusy(1);
  1130. end;
  1131. function TCustomDriveView.IsBusy: Boolean;
  1132. begin
  1133. Result := DoBusy(0);
  1134. end;
  1135. procedure TCustomDriveView.EndBusy;
  1136. begin
  1137. DoBusy(-1);
  1138. end;
  1139. end.