CustomDriveView.pas 39 KB

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