DriveView.pas 80 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836
  1. unit DriveView;
  2. {==================================================================
  3. Component TDriveView / Version 2.6, January 2000
  4. ==================================================================
  5. Description:
  6. ============
  7. Displays the the directory structure of all drives as treeview
  8. with shell icons. Complete drag&Drop support for files and
  9. directories.
  10. Author:
  11. =======
  12. (c) Ingo Eckel 1998, 1999
  13. Sodener Weg 38
  14. 65812 Bad Soden
  15. Germany
  16. Modifications (for WinSCP):
  17. ===========================
  18. (c) Martin Prikryl 2004
  19. V2.6:
  20. - Shows "shared"-symbol with directories
  21. - Delphi5 compatible
  22. For detailed documentation and history see TDriveView.htm.
  23. {==================================================================}
  24. interface
  25. { Define ENHVALIDATE to scan all existing directories on a detected filesystem change:}
  26. {.$DEFINE ENHVALIDATE}
  27. {Required compiler options for TDriveView:}
  28. {$A+,B-,X+,H+,P+}
  29. {$WARN SYMBOL_PLATFORM OFF}
  30. uses
  31. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, ComObj,
  32. Dialogs, ComCtrls, ShellApi, CommCtrl, ExtCtrls, ActiveX, ShlObj,
  33. DirView, ShellDialogs, DragDrop, DragDropFilesEx, FileChanges, FileOperator,
  34. DiscMon, IEDriveInfo, IEListView, PIDL, BaseUtils, ListExt, CustomDirView,
  35. CustomDriveView, System.Generics.Collections;
  36. const
  37. msThreadChangeDelay = 50;
  38. ErrorNodeNA = '%s: Node not assigned';
  39. {Flags used by TDriveView.RefreshRootNodes:}
  40. dvdsFloppy = 8; {Include floppy drives}
  41. dvdsRereadAllways = 16; {Refresh drivestatus in any case}
  42. WM_USER_SHCHANGENOTIFY = WM_USER + $2000 + 13;
  43. type
  44. EInvalidDirName = class(Exception);
  45. ENodeNotAssigned = class(Exception);
  46. TDriveStatus = class
  47. Scanned: Boolean; {Drive allready scanned?}
  48. Verified: Boolean; {Drive completly scanned?}
  49. RootNode: TTreeNode; {Rootnode to drive}
  50. RootNodeIndex: Integer;
  51. DiscMonitor: TDiscMonitor; {Monitor thread}
  52. ChangeTimer: TTimer; {Change timer for the monitor thread}
  53. DefaultDir: string; {Current directory}
  54. DriveHandle: THandle;
  55. NotificationHandle: HDEVNOTIFY;
  56. end;
  57. TDriveStatusPair = TPair<string, TDriveStatus>;
  58. TScanDirInfo = record
  59. SearchNewDirs: Boolean;
  60. StartNode: TTreeNode;
  61. DriveType: Integer;
  62. end;
  63. PScanDirInfo = ^TScanDirInfo;
  64. TDriveView = class;
  65. TNodeData = class
  66. private
  67. FDirName: string;
  68. FShortName: string;
  69. FAttr: Integer;
  70. FScanned: Boolean;
  71. FData: Pointer;
  72. FIsRecycleBin: Boolean;
  73. FIconEmpty: Boolean;
  74. public
  75. shAttr: ULONG;
  76. PIDL: PItemIDList;
  77. ShellFolder: IShellFolder;
  78. constructor Create;
  79. destructor Destroy; override;
  80. property DirName: string read FDirName write FDirName;
  81. property ShortName: string read FShortName write FShortName;
  82. property Attr: Integer read FAttr write FAttr;
  83. property Scanned: Boolean read FScanned write FScanned;
  84. property Data: Pointer read FData write FData;
  85. property IsRecycleBin: Boolean read FIsRecycleBin;
  86. property IconEmpty: Boolean read FIconEmpty write FIconEmpty;
  87. end;
  88. TDriveTreeNode = class(TTreeNode)
  89. procedure Assign(Source: TPersistent); override;
  90. end;
  91. TDriveView = class(TCustomDriveView)
  92. private
  93. FDriveStatus: TObjectDictionary<string, TDriveStatus>;
  94. FConfirmDelete: Boolean;
  95. FConfirmOverwrite: Boolean;
  96. FWatchDirectory: Boolean;
  97. FDirectory: string;
  98. FFullDriveScan: Boolean;
  99. FShowVolLabel: Boolean;
  100. FVolDisplayStyle: TVolumeDisplayStyle;
  101. FChangeFlag: Boolean;
  102. FLastDir: string;
  103. FValidateFlag: Boolean;
  104. FCreating: Boolean;
  105. FForceRename: Boolean;
  106. FRenameNode: TTreeNode;
  107. FLastRenameName: string;
  108. FInternalWindowHandle: HWND;
  109. FChangeNotify: ULONG;
  110. FPrevSelected: TTreeNode;
  111. FPrevSelectedIndex: Integer;
  112. FChangeTimerSuspended: Integer;
  113. FDesktop: IShellFolder;
  114. {Additional events:}
  115. FOnDisplayContextMenu: TNotifyEvent;
  116. FOnRefreshDrives: TNotifyEvent;
  117. FOnNeedHiddenDirectories: TNotifyEvent;
  118. {used components:}
  119. FDirView: TDirView;
  120. FFileOperator: TFileOperator;
  121. FChangeInterval: Cardinal;
  122. {Drag&drop:}
  123. FLastPathCut: string;
  124. {Drag&drop helper functions:}
  125. procedure SignalDirDelete(Sender: TObject; Files: TStringList);
  126. function CheckForSubDirs(Path: string): Boolean;
  127. function ReadSubDirs(Node: TTreeNode; DriveType: Integer): Boolean;
  128. {Callback-functions used by iteratesubtree:}
  129. function CallBackValidateDir(var Node: TTreeNode; Data: Pointer): Boolean;
  130. { Notification procedures used by component TDiscMonitor: }
  131. procedure ChangeDetected(Sender: TObject; const Directory: string;
  132. var SubdirsChanged: Boolean);
  133. procedure ChangeInvalid(Sender: TObject; const Directory: string; const ErrorStr: string);
  134. {Notification procedure used by component TTimer:}
  135. procedure ChangeTimerOnTimer(Sender: TObject);
  136. protected
  137. procedure SetSelected(Node: TTreeNode);
  138. procedure SetFullDriveScan(DoFullDriveScan: Boolean);
  139. procedure SetWatchDirectory(Value: Boolean);
  140. procedure SetShowVolLabel(ShowIt: Boolean);
  141. procedure SetDirView(Value: TDirView);
  142. procedure SetDirectory(Value: string); override;
  143. procedure GetNodeShellAttr(ParentNode: TTreeNode; NodeData: TNodeData; GetAttr: Boolean);
  144. function DoScanDir(FromNode: TTreeNode): Boolean;
  145. function AddChildNode(ParentNode: TTreeNode; SRec: TSearchRec): TTreeNode;
  146. procedure CreateWatchThread(Drive: string);
  147. function NodeWatched(Node: TTreeNode): Boolean;
  148. procedure TerminateWatchThread(Drive: string);
  149. function WatchThreadActive: Boolean; overload;
  150. function WatchThreadActive(Drive: string): Boolean; overload;
  151. procedure InternalWndProc(var Msg: TMessage);
  152. procedure UpdateDriveNotifications(Drive: string);
  153. procedure DriveRemoved(Drive: string);
  154. procedure DriveRemoving(Drive: string);
  155. procedure CancelDriveRefresh;
  156. procedure ScheduleDriveRefresh;
  157. function DirAttrMask: Integer;
  158. function CreateDriveStatus: TDriveStatus;
  159. procedure ValidateDirectoryEx(Node: TTreeNode; Recurse: TRecursiveScan;
  160. NewDirs: Boolean); override;
  161. procedure RebuildTree; override;
  162. procedure SetLastPathCut(Path: string);
  163. function GetCanUndoCopyMove: Boolean; virtual;
  164. procedure CreateWnd; override;
  165. procedure DestroyWnd; override;
  166. procedure Edit(const Item: TTVItem); override;
  167. procedure WMUserRename(var Message: TMessage); message WM_USER_RENAME;
  168. function GetCustomDirView: TCustomDirView; override;
  169. procedure SetCustomDirView(Value: TCustomDirView); override;
  170. function NodePath(Node: TTreeNode): string; override;
  171. function NodeIsRecycleBin(Node: TTreeNode): Boolean; override;
  172. function NodePathExists(Node: TTreeNode): Boolean; override;
  173. function NodeColor(Node: TTreeNode): TColor; override;
  174. function FindPathNode(Path: string): TTreeNode; override;
  175. function CreateNode: TTreeNode; override;
  176. function DDSourceEffects: TDropEffectSet; override;
  177. procedure DDChooseEffect(KeyState: Integer; var Effect: Integer; PreferredEffect: Integer); override;
  178. function DragCompleteFileList: Boolean; override;
  179. function DDExecute: TDragResult; override;
  180. public
  181. property Images;
  182. property StateImages;
  183. property Items stored False;
  184. property Selected Write SetSelected stored False;
  185. property DragImageList: TDragImageList read FDragImageList;
  186. property CanUndoCopyMove: Boolean read GetCanUndoCopyMove;
  187. property DDFileOperator: TFileOperator read FFileOperator;
  188. property LastPathCut: string read FLastPathCut write SetLastPathCut;
  189. function UndoCopyMove: Boolean; dynamic;
  190. procedure EmptyClipboard; dynamic;
  191. function CopyToClipBoard(Node: TTreeNode): Boolean; dynamic;
  192. function CutToClipBoard(Node: TTreeNode): Boolean; dynamic;
  193. function CanPasteFromClipBoard: Boolean; dynamic;
  194. function PasteFromClipBoard(TargetPath: string = ''): Boolean; dynamic;
  195. procedure PerformDragDropFileOperation(Node: TTreeNode; Effect: Integer); override;
  196. {Drive handling:}
  197. function GetDriveStatus(Drive: string): TDriveStatus;
  198. function GetDriveTypetoNode(Node: TTreeNode): Integer; {Returns DRIVE_CDROM etc..}
  199. function GetDriveToNode(Node: TTreeNode): string;
  200. function GetDriveText(Drive: string): string;
  201. procedure ScanDrive(Drive: string);
  202. procedure RefreshRootNodes(dsFlags: Integer);
  203. function GetDrives: TStrings;
  204. {Node handling:}
  205. procedure SetImageIndex(Node: TTreeNode); virtual;
  206. function FindNodeToPath(Path: string): TTreeNode;
  207. function RootNode(Node: TTreeNode): TTreeNode;
  208. function GetDirName(Node: TTreeNode): string;
  209. function GetDisplayName(Node: TTreeNode): string;
  210. function NodePathName(Node: TTreeNode): string; override;
  211. function GetFQPIDL(Node: TTreeNode): PItemIDList;
  212. constructor Create(AOwner: TComponent); override;
  213. destructor Destroy; override;
  214. {Menu-handling:}
  215. procedure DisplayContextMenu(Node: TTreeNode; Point: TPoint); override;
  216. procedure DisplayPropertiesMenu(Node: TTreeNode); override;
  217. {Watchthread handling:}
  218. procedure StartWatchThread;
  219. procedure StopWatchThread;
  220. procedure SuspendChangeTimer;
  221. procedure ResumeChangeTimer;
  222. procedure StartAllWatchThreads;
  223. procedure StopAllWatchThreads;
  224. procedure ValidateCurrentDirectoryIfNotMonitoring;
  225. (* Modified Events: *)
  226. procedure GetImageIndex(Node: TTreeNode); override;
  227. function CanEdit(Node: TTreeNode): Boolean; override;
  228. function CanChange(Node: TTreeNode): Boolean; override;
  229. function CanExpand(Node: TTreeNode): Boolean; override;
  230. procedure Delete(Node: TTreeNode); override;
  231. procedure Loaded; override;
  232. procedure KeyPress(var Key: Char); override;
  233. procedure Change(Node: TTreeNode); override;
  234. published
  235. {Additional properties:}
  236. {Current selected directory:}
  237. property Directory;
  238. {Confirm deleting directories:}
  239. property ConfirmDelete: Boolean read FConfirmDelete write FConfirmDelete default True;
  240. {Confirm overwriting directories:}
  241. property ConfirmOverwrite: Boolean read FConfirmOverwrite write FConfirmOverwrite default True;
  242. {Scan all directories in method ScanDrive:}
  243. property FullDriveScan: Boolean read FFullDriveScan write SetFullDriveScan default False;
  244. {Enable automatic update on filesystem changes:}
  245. property WatchDirectory: Boolean read FWatchDirectory write SetWatchDirectory default False;
  246. {Linked component TDirView:}
  247. property DirView: TDirView read FDirView write SetDirView;
  248. {Show the volume labels of drives:}
  249. property ShowVolLabel: Boolean read FShowVolLabel write SetShowVolLabel default True;
  250. {Additional events:}
  251. property OnDisplayContextMenu: TNotifyEvent read FOnDisplayContextMenu
  252. write FOnDisplayContextMenu;
  253. property OnRefreshDrives: TNotifyEvent read FOnRefreshDrives
  254. write FOnRefreshDrives;
  255. property OnBusy;
  256. property DDLinkOnExeDrag;
  257. property TargetPopUpMenu;
  258. property OnDDDragEnter;
  259. property OnDDDragLeave;
  260. property OnDDDragOver;
  261. property OnDDDrop;
  262. property OnDDQueryContinueDrag;
  263. property OnDDGiveFeedback;
  264. property OnDDDragDetect;
  265. property OnDDProcessDropped;
  266. property OnDDError;
  267. property OnDDExecuted;
  268. property OnDDFileOperation;
  269. property OnDDFileOperationExecuted;
  270. property Align;
  271. property Anchors;
  272. property AutoExpand;
  273. property BiDiMode;
  274. property BorderStyle;
  275. property BorderWidth;
  276. property ChangeDelay;
  277. property Color;
  278. property Ctl3D;
  279. property Constraints;
  280. property DoubleBuffered;
  281. {Delphi's drag&drop is not compatible with the OLE windows drag&drop:}
  282. property DragKind;
  283. property DragCursor;
  284. property DragMode Default dmAutomatic;
  285. property OnDragDrop;
  286. property OnDragOver;
  287. property Enabled;
  288. property Font;
  289. property HideSelection;
  290. property HotTrack;
  291. property Indent;
  292. property ParentBiDiMode;
  293. property ParentColor;
  294. property ParentCtl3D;
  295. property ParentDoubleBuffered;
  296. property ParentFont;
  297. property ParentShowHint;
  298. property PopupMenu;
  299. property ReadOnly;
  300. property RightClickSelect;
  301. property RowSelect;
  302. property ShowButtons;
  303. property ShowHint;
  304. property ShowLines;
  305. property TabOrder;
  306. property TabStop default True;
  307. property ToolTips;
  308. property Visible;
  309. property OnChange;
  310. property OnChanging;
  311. property OnClick;
  312. property OnCollapsing;
  313. property OnCollapsed;
  314. property OnCompare;
  315. property OnDblClick;
  316. property OnDeletion;
  317. property OnEdited;
  318. property OnEditing;
  319. property OnEndDock;
  320. property OnEndDrag;
  321. property OnEnter;
  322. property OnExit;
  323. property OnExpanding;
  324. property OnExpanded;
  325. property OnGetImageIndex;
  326. property OnGetSelectedIndex;
  327. property OnKeyDown;
  328. property OnKeyPress;
  329. property OnKeyUp;
  330. property OnMouseDown;
  331. property OnMouseMove;
  332. property OnMouseUp;
  333. property OnStartDock;
  334. property OnStartDrag;
  335. property OnNeedHiddenDirectories: TNotifyEvent read FOnNeedHiddenDirectories write FOnNeedHiddenDirectories;
  336. end;
  337. procedure Register;
  338. implementation
  339. uses
  340. CompThread, PasTools, UITypes, Types, OperationWithTimeout, System.Generics.Defaults;
  341. type
  342. PInt = ^Integer;
  343. procedure Register;
  344. begin
  345. RegisterComponents('DriveDir', [TDriveView]);
  346. end; {Register}
  347. constructor TNodeData.Create;
  348. begin
  349. inherited;
  350. FAttr := 0;
  351. FScanned := False;
  352. FDirName := '';
  353. FShortName := '';
  354. FIsRecycleBin := False;
  355. FIconEmpty := True;
  356. shAttr := 0;
  357. PIDL := nil;
  358. ShellFolder := nil;
  359. end; {TNodeData.Create}
  360. destructor TNodeData.Destroy;
  361. begin
  362. SetLength(FDirName, 0);
  363. if Assigned(PIDL) then
  364. FreePIDL(PIDL);
  365. inherited;
  366. end; {TNodeData.Destroy}
  367. { TDriveTreeNode }
  368. procedure TDriveTreeNode.Assign(Source: TPersistent);
  369. var
  370. SourceData: TNodeData;
  371. NewData: TNodeData;
  372. begin
  373. inherited Assign(Source);
  374. if not Deleting and (Source is TTreeNode) then
  375. begin
  376. SourceData := TNodeData(TTreeNode(Source).Data);
  377. NewData := TNodeData.Create();
  378. NewData.DirName := SourceData.DirName;
  379. NewData.ShortName := SourceData.ShortName;
  380. NewData.Attr := SourceData.Attr;
  381. NewData.Scanned := SourceData.Scanned;
  382. NewData.Data := SourceData.Data;
  383. NewData.FIsRecycleBin := SourceData.FIsRecycleBin;
  384. NewData.IconEmpty := SourceData.IconEmpty;
  385. TTreeNode(Source).Data := NewData;
  386. end;
  387. end;
  388. { TDriveView }
  389. constructor TDriveView.Create(AOwner: TComponent);
  390. var
  391. Drive: TRealDrive;
  392. ChangeNotifyEntry: TSHChangeNotifyEntry;
  393. Dummy: string;
  394. begin
  395. inherited;
  396. FCreating := True;
  397. FDriveStatus := TObjectDictionary<string, TDriveStatus>.Create([doOwnsValues]);
  398. FChangeInterval := MSecsPerSec;
  399. for Drive := FirstDrive to LastDrive do
  400. begin
  401. FDriveStatus.Add(Drive, CreateDriveStatus);
  402. end;
  403. FFileOperator := TFileOperator.Create(Self);
  404. FFileOperator.Flags := [foAllowUndo, foNoConfirmMkDir];
  405. FShowVolLabel := True;
  406. FChangeFlag := False;
  407. FLastDir := EmptyStr;
  408. FValidateFlag := False;
  409. FConfirmDelete := True;
  410. FDirectory := EmptyStr;
  411. FForceRename := False;
  412. FLastRenameName := '';
  413. FRenameNode := nil;
  414. FPrevSelected := nil;
  415. FPrevSelectedIndex := -1;
  416. FChangeTimerSuspended := 0;
  417. FConfirmOverwrite := True;
  418. FLastPathCut := '';
  419. FStartPos.X := -1;
  420. FStartPos.Y := -1;
  421. FDragPos := FStartPos;
  422. FInternalWindowHandle := Classes.AllocateHWnd(InternalWndProc);
  423. // Source: petr.solin 2022-02-25
  424. FChangeNotify := 0;
  425. if SpecialFolderLocation(CSIDL_DESKTOP, Dummy, ChangeNotifyEntry.pidl) then
  426. begin
  427. ChangeNotifyEntry.fRecursive := False;
  428. FChangeNotify :=
  429. SHChangeNotifyRegister(
  430. FInternalWindowHandle, SHCNRF_ShellLevel or SHCNRF_NewDelivery,
  431. SHCNE_RENAMEFOLDER or SHCNE_MEDIAINSERTED or SHCNE_MEDIAREMOVED,
  432. WM_USER_SHCHANGENOTIFY, 1, ChangeNotifyEntry);
  433. end;
  434. with FDragDropFilesEx do
  435. begin
  436. ShellExtensions.DragDropHandler := True;
  437. end;
  438. end; {Create}
  439. destructor TDriveView.Destroy;
  440. var
  441. DriveStatusPair: TDriveStatusPair;
  442. begin
  443. if FChangeNotify <> 0 then SHChangeNotifyDeregister(FChangeNotify);
  444. Classes.DeallocateHWnd(FInternalWindowHandle);
  445. for DriveStatusPair in FDriveStatus do
  446. begin
  447. with DriveStatusPair.Value do
  448. begin
  449. if Assigned(DiscMonitor) then
  450. FreeAndNil(DiscMonitor);
  451. if Assigned(ChangeTimer) then
  452. FreeAndNil(ChangeTimer);
  453. end;
  454. UpdateDriveNotifications(DriveStatusPair.Key);
  455. end;
  456. FDriveStatus.Free;
  457. if Assigned(FFileOperator) then
  458. FFileOperator.Free;
  459. inherited Destroy;
  460. end; {Destroy}
  461. function TDriveView.CreateDriveStatus: TDriveStatus;
  462. begin
  463. Result := TDriveStatus.Create;
  464. with Result do
  465. begin
  466. Scanned := False;
  467. Verified := False;
  468. RootNode := nil;
  469. RootNodeIndex := -1;
  470. DiscMonitor := nil;
  471. DefaultDir := EmptyStr;
  472. {ChangeTimer: }
  473. ChangeTimer := TTimer.Create(Self);
  474. ChangeTimer.Interval := 0;
  475. ChangeTimer.Enabled := False;
  476. ChangeTimer.OnTimer := ChangeTimerOnTimer;
  477. DriveHandle := INVALID_HANDLE_VALUE;
  478. NotificationHandle := nil;
  479. end;
  480. end;
  481. procedure TDriveView.DriveRemoving(Drive: string);
  482. begin
  483. DriveRemoved(Drive);
  484. TerminateWatchThread(Drive);
  485. end;
  486. type
  487. PDevBroadcastHdr = ^TDevBroadcastHdr;
  488. TDevBroadcastHdr = record
  489. dbch_size: DWORD;
  490. dbch_devicetype: DWORD;
  491. dbch_reserved: DWORD;
  492. end;
  493. PDevBroadcastVolume = ^TDevBroadcastVolume;
  494. TDevBroadcastVolume = record
  495. dbcv_size: DWORD;
  496. dbcv_devicetype: DWORD;
  497. dbcv_reserved: DWORD;
  498. dbcv_unitmask: DWORD;
  499. dbcv_flags: WORD;
  500. end;
  501. PDEV_BROADCAST_HANDLE = ^DEV_BROADCAST_HANDLE;
  502. DEV_BROADCAST_HANDLE = record
  503. dbch_size : DWORD;
  504. dbch_devicetype : DWORD;
  505. dbch_reserved : DWORD;
  506. dbch_handle : THandle;
  507. dbch_hdevnotify : HDEVNOTIFY ;
  508. dbch_eventguid : TGUID;
  509. dbch_nameoffset : LongInt;
  510. dbch_data : Byte;
  511. end;
  512. PPItemIDList = ^PItemIDList;
  513. const
  514. DBT_DEVTYP_HANDLE = $00000006;
  515. DBT_CONFIGCHANGED = $0018;
  516. DBT_DEVICEARRIVAL = $8000;
  517. DBT_DEVICEQUERYREMOVE = $8001;
  518. DBT_DEVICEREMOVEPENDING = $8003;
  519. DBT_DEVICEREMOVECOMPLETE = $8004;
  520. DBT_DEVTYP_VOLUME = $00000002;
  521. // WORKAROUND Declaration in Winapi.ShlObj.pas is wrong
  522. function SHChangeNotification_Lock(hChange: THandle; dwProcId: DWORD;
  523. var PPidls: PPItemIDList; var plEvent: Longint): THANDLE; stdcall;
  524. external 'shell32.dll' name 'SHChangeNotification_Lock';
  525. procedure TDriveView.InternalWndProc(var Msg: TMessage);
  526. var
  527. DeviceType: DWORD;
  528. UnitMask: DWORD;
  529. DeviceHandle: THandle;
  530. Drive: Char;
  531. DriveStatusPair: TDriveStatusPair;
  532. PPIDL: PPItemIDList;
  533. Event: LONG;
  534. Lock: THandle;
  535. begin
  536. with Msg do
  537. begin
  538. if Msg = WM_USER_SHCHANGENOTIFY then
  539. begin
  540. Lock := SHChangeNotification_Lock(wParam, lParam, PPIDL, Event);
  541. try
  542. if (Event = SHCNE_RENAMEFOLDER) or // = drive rename
  543. (Event = SHCNE_MEDIAINSERTED) or // also bitlocker drive unlock (also sends SHCNE_UPDATEDIR)
  544. (Event = SHCNE_MEDIAREMOVED) then
  545. begin
  546. ScheduleDriveRefresh;
  547. end;
  548. finally
  549. SHChangeNotification_Unlock(Lock);
  550. end;
  551. end
  552. else
  553. if Msg = WM_DEVICECHANGE then
  554. begin
  555. if (wParam = DBT_CONFIGCHANGED) or
  556. (wParam = DBT_DEVICEARRIVAL) or
  557. (wParam = DBT_DEVICEREMOVECOMPLETE) then
  558. begin
  559. ScheduleDriveRefresh;
  560. end
  561. else
  562. if (wParam = DBT_DEVICEQUERYREMOVE) or
  563. (wParam = DBT_DEVICEREMOVEPENDING) then
  564. begin
  565. DeviceType := PDevBroadcastHdr(lParam)^.dbch_devicetype;
  566. // This is specifically for VeraCrypt.
  567. // For normal drives, see DBT_DEVTYP_HANDLE below
  568. // (and maybe now that we have generic implementation, this specific code for VeraCrypt might not be needed anymore)
  569. if DeviceType = DBT_DEVTYP_VOLUME then
  570. begin
  571. UnitMask := PDevBroadcastVolume(lParam)^.dbcv_unitmask;
  572. Drive := FirstDrive;
  573. while UnitMask > 0 do
  574. begin
  575. if UnitMask and $01 <> 0 then
  576. begin
  577. DriveRemoving(Drive);
  578. end;
  579. UnitMask := UnitMask shr 1;
  580. Drive := Chr(Ord(Drive) + 1);
  581. end;
  582. end
  583. else
  584. if DeviceType = DBT_DEVTYP_HANDLE then
  585. begin
  586. DeviceHandle := PDEV_BROADCAST_HANDLE(lParam)^.dbch_handle;
  587. for DriveStatusPair in FDriveStatus do
  588. if DriveStatusPair.Value.DriveHandle = DeviceHandle then
  589. begin
  590. DriveRemoving(DriveStatusPair.Key);
  591. end;
  592. end;
  593. end;
  594. end
  595. else
  596. if Msg = WM_TIMER then
  597. begin
  598. CancelDriveRefresh;
  599. try
  600. //DriveInfo.Load;
  601. RefreshRootNodes(dsAll or dvdsRereadAllways);
  602. if Assigned(OnRefreshDrives) then
  603. OnRefreshDrives(Self);
  604. except
  605. Application.HandleException(Self);
  606. end;
  607. end;
  608. Result := DefWindowProc(FInternalWindowHandle, Msg, wParam, lParam);
  609. end;
  610. end;
  611. procedure TDriveView.CancelDriveRefresh;
  612. begin
  613. KillTimer(FInternalWindowHandle, 1);
  614. end;
  615. procedure TDriveView.ScheduleDriveRefresh;
  616. begin
  617. CancelDriveRefresh;
  618. // Delay refreshing drives for a sec.
  619. // Particularly with CD/DVD drives, if we query display name
  620. // immediately after receiving DBT_DEVICEARRIVAL, we do not get media label.
  621. // Actually one sec does not help usually, but we do not want to wait any longer,
  622. // because we want to add USB drives asap.
  623. // And this problem might be solved now by SHChangeNotifyRegister/SHCNE_RENAMEFOLDER.
  624. SetTimer(FInternalWindowHandle, 1, MSecsPerSec, nil);
  625. end;
  626. procedure TDriveView.CreateWnd;
  627. var
  628. DriveStatus: TDriveStatus;
  629. begin
  630. inherited;
  631. if Assigned(PopupMenu) then
  632. PopupMenu.Autopopup := False;
  633. OLECheck(SHGetDesktopFolder(FDesktop));
  634. FDragDropFilesEx.SourceEffects := [deCopy, deMove, deLink];
  635. FDragDropFilesEx.TargetEffects := [deCopy, deMove, deLink];
  636. if FPrevSelectedIndex >= 0 then
  637. begin
  638. FPrevSelected := Items[FPrevSelectedIndex];
  639. FPrevSelectedIndex := -1;
  640. end;
  641. for DriveStatus in FDriveStatus.Values do
  642. with DriveStatus do
  643. begin
  644. if RootNodeIndex >= 0 then
  645. begin
  646. RootNode := Items[RootNodeIndex];
  647. RootNodeIndex := -1;
  648. end;
  649. end;
  650. end; {CreateWnd}
  651. procedure TDriveView.DestroyWnd;
  652. var
  653. DriveStatus: TDriveStatus;
  654. begin
  655. if CreateWndRestores and (Items.Count > 0) and (csRecreating in ControlState) then
  656. begin
  657. FPrevSelectedIndex := -1;
  658. if Assigned(FPrevSelected) then
  659. begin
  660. FPrevSelectedIndex := FPrevSelected.AbsoluteIndex;
  661. FPrevSelected := nil;
  662. end;
  663. for DriveStatus in FDriveStatus.Values do
  664. with DriveStatus do
  665. begin
  666. RootNodeIndex := -1;
  667. if Assigned(RootNode) then
  668. begin
  669. RootNodeIndex := RootNode.AbsoluteIndex;
  670. RootNode := nil;
  671. end;
  672. end;
  673. end;
  674. inherited;
  675. end;
  676. function TDriveView.GetFQPIDL(Node: TTreeNode): PItemIDList;
  677. var
  678. Eaten: ULONG;
  679. shAttr: ULONG;
  680. begin
  681. Result := nil;
  682. if Assigned(Node) then
  683. begin
  684. shAttr := 0;
  685. FDesktop.ParseDisplayName(FParentForm.Handle, nil, PChar(NodePathName(Node)), Eaten,
  686. Result, shAttr);
  687. end;
  688. end; {GetFQPIDL}
  689. function TDriveView.NodeColor(Node: TTreeNode): TColor;
  690. begin
  691. Result := clDefaultItemColor;
  692. with TNodeData(Node.Data) do
  693. if not Node.Selected then
  694. begin
  695. {Colored display of compressed directories:}
  696. if (Attr and FILE_ATTRIBUTE_COMPRESSED) <> 0 then
  697. begin
  698. if SupportsDarkMode and DarkMode then Result := clSkyBlue
  699. else Result := clBlue;
  700. end
  701. else
  702. {Dimmed display, if hidden-atrribut set:}
  703. if FDimmHiddenDirs and ((Attr and FILE_ATTRIBUTE_HIDDEN) <> 0) then
  704. Result := clGrayText
  705. end;
  706. end;
  707. function TDriveView.GetCustomDirView: TCustomDirView;
  708. begin
  709. Result := DirView;
  710. end;
  711. procedure TDriveView.SetCustomDirView(Value: TCustomDirView);
  712. begin
  713. DirView := Value as TDirView;
  714. end;
  715. function TDriveView.NodePath(Node: TTreeNode): string;
  716. var
  717. ParentNode: TTreeNode;
  718. begin
  719. if not Assigned(Node) then
  720. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['GetDirPath']));
  721. Result := GetDirName(Node);
  722. ParentNode := Node.Parent;
  723. while (ParentNode <> nil) and (ParentNode.Level >= 0) do
  724. begin
  725. if ParentNode.Level > 0 then
  726. Result := GetDirName(ParentNode) + '\' + Result
  727. else
  728. Result := GetDirName(ParentNode) + Result;
  729. ParentNode := ParentNode.Parent;
  730. end;
  731. if IsRootPath(Result) then
  732. Result := ExcludeTrailingBackslash(Result);
  733. end;
  734. {NodePathName: Returns the complete path to Node with trailing backslash on rootnodes:
  735. C:\ ,C:\WINDOWS, C:\WINDOWS\SYSTEM }
  736. function TDriveView.NodePathName(Node: TTreeNode): string;
  737. begin
  738. Result := NodePath(Node);
  739. if IsRootPath(Result) then
  740. Result := IncludeTrailingBackslash(Result);
  741. end; {NodePathName}
  742. function TDriveView.NodeIsRecycleBin(Node: TTreeNode): Boolean;
  743. begin
  744. Result := TNodeData(Node.Data).IsRecycleBin;
  745. end;
  746. function TDriveView.NodePathExists(Node: TTreeNode): Boolean;
  747. begin
  748. Result := DirectoryExists(NodePathName(Node));
  749. end;
  750. function TDriveView.CanEdit(Node: TTreeNode): Boolean;
  751. begin
  752. Result := inherited CanEdit(Node) or FForceRename;
  753. if Result then
  754. begin
  755. Result := Assigned(Node.Parent) and
  756. (not TNodeData(Node.Data).IsRecycleBin) and
  757. (not ReadOnly) and
  758. (FDragDropFilesEx.DragDetectStatus <> ddsDrag) and
  759. ((TNodeData(Node.Data).Attr and (faReadOnly or faSysFile)) = 0) and
  760. (UpperCase(Node.Text) = UpperCase(GetDirName(Node)));
  761. end;
  762. FForceRename := False;
  763. end; {CanEdit}
  764. procedure TDriveView.Edit(const Item: TTVItem);
  765. var
  766. SRec: TSearchRec;
  767. Node: TTreeNode;
  768. Info: string;
  769. i: Integer;
  770. begin
  771. Node := GetNodeFromHItem(Item);
  772. if (Length(Item.pszText) > 0) and (Item.pszText <> Node.Text) then
  773. begin
  774. if StrContains(coInvalidDosChars, Item.pszText) then
  775. begin
  776. Info := coInvalidDosChars;
  777. for i := Length(Info) downto 1 do
  778. System.Insert(Space, Info, i);
  779. if Length(Item.pszText) > 0 then
  780. raise EInvalidDirName.Create(SErrorInvalidName + Space + Info);
  781. Exit;
  782. end;
  783. StopWatchThread;
  784. if Assigned(DirView) then
  785. DirView.StopWatchThread;
  786. with FFileOperator do
  787. begin
  788. Flags := [foAllowUndo, foNoConfirmation];
  789. Operation := foRename;
  790. OperandFrom.Clear;
  791. OperandTo.Clear;
  792. OperandFrom.Add(NodePath(Node));
  793. OperandTo.Add(IncludeTrailingBackslash(NodePath(Node.Parent)) + Item.pszText);
  794. end;
  795. try
  796. if FFileOperator.Execute then
  797. begin
  798. Node.Text := Item.pszText;
  799. TNodeData(Node.Data).DirName := Item.pszText;
  800. if FindFirst(ApiPath(IncludeTrailingBackslash(NodePath(Node.Parent)) + Item.pszText),
  801. faAnyFile, SRec) = 0 then
  802. begin
  803. TNodeData(Node.Data).ShortName := string(SRec.FindData.cAlternateFileName);
  804. end;
  805. FindClose(SRec);
  806. SortChildren(Node.Parent, False);
  807. inherited;
  808. end
  809. else
  810. begin
  811. if FileOrDirExists(IncludeTrailingBackslash(NodePath(Node.Parent)) + Item.pszText) then
  812. Info := SErrorRenameFileExists + Item.pszText
  813. else
  814. Info := SErrorRenameFile + Item.pszText;
  815. MessageBeep(MB_ICONHAND);
  816. if MessageDlg(FormatLastOSError(Info), mtError, [mbOK, mbAbort], 0) = mrOK then
  817. begin
  818. FLastRenameName := Item.pszText;
  819. FRenameNode := Node;
  820. PostMessage(Self.Handle, WM_USER_RENAME, 0, 0);
  821. end;
  822. end;
  823. finally
  824. StartWatchThread;
  825. if Assigned(DirView) then
  826. begin
  827. DirView.Reload2;
  828. DirView.StartWatchThread;
  829. end;
  830. end;
  831. end;
  832. end; {Edit}
  833. procedure TDriveView.WMUserRename(var Message: TMessage);
  834. begin
  835. if Assigned(FRenameNode) then
  836. begin
  837. FForceRename := True;
  838. TreeView_EditLabel(Handle, FRenameNode.ItemID);
  839. SetWindowText(TreeView_GetEditControl(Self.Handle), PChar(FLastRenameName));
  840. FRenameNode := nil;
  841. end;
  842. end; {WMUserRename}
  843. function TDriveView.CanExpand(Node: TTreeNode): Boolean;
  844. var
  845. SubNode: TTreeNode;
  846. Drive: string;
  847. SaveCursor: TCursor;
  848. begin
  849. Result := inherited CanExpand(Node);
  850. Drive := GetDriveToNode(Node);
  851. if Node.HasChildren then
  852. begin
  853. if (Node.Level = 0) and
  854. (not GetDriveStatus(Drive).Scanned) and
  855. DriveInfo.IsFixedDrive(Drive) then
  856. begin
  857. SubNode := Node.GetFirstChild;
  858. if not Assigned(SubNode) then
  859. begin
  860. ScanDrive(Drive);
  861. SubNode := Node.GetFirstChild;
  862. Node.HasChildren := Assigned(SubNode);
  863. Result := Node.HasChildren;
  864. if not Assigned(GetDriveStatus(Drive).DiscMonitor) then
  865. CreateWatchThread(Drive);
  866. end;
  867. end
  868. else
  869. begin
  870. SaveCursor := Screen.Cursor;
  871. Screen.Cursor := crHourGlass;
  872. try
  873. if (not TNodeData(Node.Data).Scanned) and DoScanDir(Node) then
  874. begin
  875. ReadSubDirs(Node, DriveInfo.Get(Drive).DriveType);
  876. end;
  877. finally
  878. Screen.Cursor := SaveCursor;
  879. end;
  880. end;
  881. end;
  882. end; {CanExpand}
  883. procedure TDriveView.GetImageIndex(Node: TTreeNode);
  884. begin
  885. if TNodeData(Node.Data).IconEmpty then
  886. SetImageIndex(Node);
  887. inherited;
  888. end; {GetImageIndex}
  889. procedure TDriveView.Loaded;
  890. begin
  891. inherited;
  892. {Create the drive nodes:}
  893. RefreshRootNodes(dsDisplayName or dvdsFloppy);
  894. {Set the initial directory:}
  895. if (Length(FDirectory) > 0) and DirectoryExists(FDirectory) then
  896. Directory := FDirectory;
  897. FCreating := False;
  898. end; {Loaded}
  899. function TDriveView.CreateNode: TTreeNode;
  900. begin
  901. Result := TDriveTreeNode.Create(Items);
  902. end;
  903. procedure TDriveView.Delete(Node: TTreeNode);
  904. var
  905. NodeData: TNodeData;
  906. begin
  907. if Node = FPrevSelected then
  908. FPrevSelected := nil;
  909. NodeData := nil;
  910. if Assigned(Node) and Assigned(Node.Data) then
  911. NodeData := TNodeData(Node.Data);
  912. Node.Data := nil;
  913. inherited;
  914. if Assigned(NodeData) and not (csRecreating in ControlState) then
  915. begin
  916. NodeData.Destroy;
  917. end;
  918. end; {OnDelete}
  919. procedure TDriveView.KeyPress(var Key: Char);
  920. begin
  921. inherited;
  922. if Assigned(Selected) then
  923. begin
  924. if Pos(Key, coInvalidDosChars) <> 0 then
  925. begin
  926. Beep;
  927. Key := #0;
  928. end;
  929. end;
  930. end; {KeyPress}
  931. function TDriveView.CanChange(Node: TTreeNode): Boolean;
  932. var
  933. Path: string;
  934. Drive: string;
  935. begin
  936. Result := inherited CanChange(Node);
  937. if not Reading and not (csRecreating in ControlState) then
  938. begin
  939. if Result and Assigned(Node) then
  940. begin
  941. Path := NodePathName(Node);
  942. if Path <> FLastDir then
  943. begin
  944. Drive := DriveInfo.GetDriveKey(Path);
  945. DriveInfo.ReadDriveStatus(Drive, dsSize or dsImageIndex);
  946. if not DriveInfo.Get(Drive).DriveReady then
  947. begin
  948. MessageDlg(Format(SDriveNotReady, [Drive]), mtError, [mbOK], 0);
  949. Result := False;
  950. end
  951. else
  952. try
  953. CheckCanOpenDirectory(Path);
  954. except
  955. Application.HandleException(Self);
  956. Result := False;
  957. end;
  958. end;
  959. end;
  960. if Result and (csDestroying in ComponentState) then
  961. begin
  962. Result := False;
  963. end;
  964. if Result and
  965. (not FCanChange) and
  966. Assigned(Node) and
  967. Assigned(Node.Data) and
  968. Assigned(Selected) and
  969. Assigned(Selected.Data) then
  970. begin
  971. DropTarget := Node;
  972. Result := False;
  973. end
  974. else
  975. begin
  976. DropTarget := nil;
  977. end;
  978. end;
  979. end; {CanChange}
  980. procedure TDriveView.Change(Node: TTreeNode);
  981. var
  982. Drive: string;
  983. OldSerial: DWORD;
  984. NewDir: string;
  985. PrevDrive: string;
  986. begin
  987. if not Reading and not (csRecreating in ControlState) then
  988. begin
  989. if Assigned(Node) then
  990. begin
  991. NewDir := NodePathName(Node);
  992. if NewDir <> FLastDir then
  993. begin
  994. Drive := DriveInfo.GetDriveKey(NewDir);
  995. if Length(FLastDir) > 0 then
  996. PrevDrive := DriveInfo.GetDriveKey(FLastDir)
  997. else
  998. PrevDrive := '';
  999. FChangeFlag := True;
  1000. FLastDir := NewDir;
  1001. OldSerial := DriveInfo.Get(Drive).DriveSerial;
  1002. DriveInfo.ReadDriveStatus(Drive, dsSize or dsImageIndex);
  1003. with DriveInfo.Get(Drive) do
  1004. begin
  1005. if Assigned(FDirView) and (FDirView.Path <> NewDir) then
  1006. FDirView.Path := NewDir;
  1007. if DriveReady then
  1008. begin
  1009. if not DirectoryExists(NewDir) then
  1010. begin
  1011. ValidateDirectory(GetDriveStatus(Drive).RootNode);
  1012. Exit;
  1013. end;
  1014. GetDriveStatus(Drive).DefaultDir := IncludeTrailingBackslash(NewDir);
  1015. if PrevDrive <> Drive then
  1016. begin
  1017. if (PrevDrive <> '') and
  1018. (DriveInfo.Get(PrevDrive).DriveType = DRIVE_REMOVABLE) then
  1019. begin
  1020. TerminateWatchThread(PrevDrive);
  1021. end;
  1022. {Drive serial has changed or is missing: allways reread the drive:}
  1023. if (DriveSerial <> OldSerial) or (DriveSerial = 0) then
  1024. begin
  1025. if TNodeData(GetDriveStatus(Drive).RootNode.Data).Scanned then
  1026. ScanDrive(Drive);
  1027. end;
  1028. end;
  1029. StartWatchThread;
  1030. end
  1031. else {Drive not ready:}
  1032. begin
  1033. GetDriveStatus(Drive).RootNode.DeleteChildren;
  1034. GetDriveStatus(Drive).DefaultDir := EmptyStr;
  1035. end;
  1036. end;
  1037. end;
  1038. if (not Assigned(FPrevSelected)) or (not FPrevSelected.HasAsParent(Node)) then
  1039. Node.Expand(False);
  1040. FPrevSelected := Node;
  1041. ValidateCurrentDirectoryIfNotMonitoring;
  1042. end;
  1043. end;
  1044. inherited;
  1045. end; {Change}
  1046. procedure TDriveView.SetImageIndex(Node: TTreeNode);
  1047. var
  1048. FileInfo: TShFileInfo;
  1049. Drive, NodePath: string;
  1050. begin
  1051. if Assigned(Node) and TNodeData(Node.Data).IconEmpty then
  1052. begin
  1053. NodePath := NodePathName(Node);
  1054. Drive := DriveInfo.GetDriveKey(NodePath);
  1055. if Node.Level = 0 then
  1056. begin
  1057. with DriveInfo.Get(Drive) do
  1058. begin
  1059. if ImageIndex = 0 then
  1060. begin
  1061. DriveInfo.ReadDriveStatus(Drive, dsImageIndex);
  1062. Node.ImageIndex := DriveInfo.Get(Drive).ImageIndex;
  1063. end
  1064. else Node.ImageIndex := ImageIndex;
  1065. Node.SelectedIndex := Node.ImageIndex;
  1066. end;
  1067. end
  1068. else
  1069. begin
  1070. if DriveInfo.Get(Drive).DriveType = DRIVE_REMOTE then
  1071. begin
  1072. Node.ImageIndex := StdDirIcon;
  1073. Node.SelectedIndex := StdDirSelIcon;
  1074. end
  1075. else
  1076. begin
  1077. try
  1078. SHGetFileInfo(PChar(NodePath), 0, FileInfo, SizeOf(FileInfo),
  1079. SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
  1080. if (FileInfo.iIcon < Images.Count) and (FileInfo.iIcon > 0) then
  1081. begin
  1082. Node.ImageIndex := FileInfo.iIcon;
  1083. SHGetFileInfo(PChar(NodePath), 0, FileInfo, SizeOf(FileInfo),
  1084. SHGFI_SYSICONINDEX or SHGFI_SMALLICON or SHGFI_OPENICON);
  1085. Node.SelectedIndex := FileInfo.iIcon;
  1086. end
  1087. else
  1088. begin
  1089. Node.ImageIndex := StdDirIcon;
  1090. Node.SelectedIndex := StdDirSelIcon;
  1091. end;
  1092. except
  1093. Node.ImageIndex := StdDirIcon;
  1094. Node.SelectedIndex := StdDirSelIcon;
  1095. end;
  1096. end;
  1097. end;
  1098. end; {IconEmpty}
  1099. TNodeData(Node.Data).IconEmpty := False;
  1100. end; {SetImageIndex}
  1101. function TDriveView.GetDriveText(Drive: string): string;
  1102. begin
  1103. if FShowVolLabel and (Length(DriveInfo.GetPrettyName(Drive)) > 0) then
  1104. begin
  1105. case FVolDisplayStyle of
  1106. doPrettyName: Result := DriveInfo.GetPrettyName(Drive);
  1107. doDisplayName: Result := DriveInfo.GetDisplayName(Drive);
  1108. end; {Case}
  1109. end
  1110. else
  1111. begin
  1112. Result := DriveInfo.GetSimpleName(Drive);
  1113. end;
  1114. end; {GetDriveText}
  1115. procedure TDriveView.GetNodeShellAttr(ParentNode: TTreeNode; NodeData: TNodeData; GetAttr: Boolean);
  1116. var
  1117. ParentFolder: IShellFolder;
  1118. ParentData: TNodeData;
  1119. begin
  1120. NodeData.shAttr := 0;
  1121. if GetAttr then
  1122. begin
  1123. if Assigned(ParentNode) then
  1124. begin
  1125. ParentData := TNodeData(ParentNode.Data);
  1126. if not Assigned(ParentData) then
  1127. begin
  1128. Assert(False);
  1129. ParentFolder := nil;
  1130. end
  1131. else
  1132. begin
  1133. if not Assigned(ParentData.ShellFolder) then
  1134. begin
  1135. GetNodeShellAttr(ParentNode.Parent, ParentData, GetAttr);
  1136. end;
  1137. ParentFolder := ParentData.ShellFolder;
  1138. end;
  1139. end
  1140. else
  1141. begin
  1142. ParentFolder := FDesktop;
  1143. end;
  1144. if Assigned(ParentFolder) and Assigned(NodeData) then
  1145. begin
  1146. if not Assigned(NodeData.PIDL) then
  1147. NodeData.PIDL := PIDL_GetFromParentFolder(ParentFolder, PChar(NodeData.DirName));
  1148. if Assigned(NodeData.PIDL) then
  1149. begin
  1150. NodeData.shAttr := SFGAO_CONTENTSMASK;
  1151. // Previously we would also make use of SFGAO_SHARE to display a share overlay.
  1152. // But for directories, Windows File Explorer does not display the overlay anymore (probably since Vista).
  1153. // And for drives (where Explorer does display the overlay), it did not work ever since we use "desktop"
  1154. // (and not "workspace" as before) to resolve drive interface (see Bug 1717).
  1155. if not Succeeded(ShellFolderGetAttributesOfWithTimeout(ParentFolder, 1, NodeData.PIDL, NodeData.shAttr, MSecsPerSec)) then
  1156. begin
  1157. NodeData.shAttr := 0;
  1158. end;
  1159. if not Assigned(NodeData.ShellFolder) then
  1160. begin
  1161. ParentFolder.BindToObject(NodeData.PIDL, nil, IID_IShellFolder, Pointer(NodeData.ShellFolder));
  1162. end;
  1163. end
  1164. end;
  1165. end;
  1166. if NodeData.shAttr = 0 then
  1167. begin
  1168. // If we cannot resolve attrs, we do not want to assume that the folder has no subfolders,
  1169. // as that will make us scan the folder.
  1170. NodeData.shAttr := SFGAO_HASSUBFOLDER;
  1171. end;
  1172. end; {GetNodeAttr}
  1173. function CompareDrive(List: TStringList; Index1, Index2: Integer): Integer;
  1174. var
  1175. Drive1, Drive2: string;
  1176. RealDrive1, RealDrive2: Boolean;
  1177. begin
  1178. Drive1 := List[Index1];
  1179. Drive2 := List[Index2];
  1180. RealDrive1 := DriveInfo.IsRealDrive(Drive1);
  1181. RealDrive2 := DriveInfo.IsRealDrive(Drive2);
  1182. if RealDrive1 = RealDrive2 then
  1183. begin
  1184. Result := CompareText(Drive1, Drive2);
  1185. end
  1186. else
  1187. if RealDrive1 and (not RealDrive2) then
  1188. begin
  1189. Result := -1;
  1190. end
  1191. else
  1192. begin
  1193. Result := 1;
  1194. end;
  1195. end;
  1196. function TDriveView.GetDrives: TStrings;
  1197. var
  1198. DriveStatusPair: TDriveStatusPair;
  1199. Drives: TStringList;
  1200. begin
  1201. Drives := TStringList.Create;
  1202. { We could iterate only .Keys here, but that crashes IDE for some reason }
  1203. for DriveStatusPair in FDriveStatus do
  1204. begin
  1205. Drives.Add(DriveStatusPair.Key);
  1206. end;
  1207. Drives.CustomSort(CompareDrive);
  1208. Result := Drives;
  1209. end;
  1210. procedure TDriveView.DriveRemoved(Drive: string);
  1211. var
  1212. NewDrive: Char;
  1213. begin
  1214. if (Directory <> '') and (Directory[1] = Drive) then
  1215. begin
  1216. if DriveInfo.IsRealDrive(Drive) then NewDrive := Drive[1]
  1217. else NewDrive := SystemDrive;
  1218. repeat
  1219. if NewDrive < SystemDrive then NewDrive := SystemDrive
  1220. else
  1221. if NewDrive = SystemDrive then NewDrive := LastDrive
  1222. else Dec(NewDrive);
  1223. DriveInfo.ReadDriveStatus(NewDrive, dsSize or dsImageIndex);
  1224. if NewDrive = Drive then
  1225. begin
  1226. Break;
  1227. end;
  1228. if DriveInfo.Get(NewDrive).Valid and DriveInfo.Get(NewDrive).DriveReady and Assigned(GetDriveStatus(NewDrive).RootNode) then
  1229. begin
  1230. Directory := NodePathName(GetDriveStatus(NewDrive).RootNode);
  1231. break;
  1232. end;
  1233. until False;
  1234. if not Assigned(Selected) then
  1235. begin
  1236. Directory := NodePathName(GetDriveStatus(SystemDrive).RootNode);
  1237. end;
  1238. end;
  1239. end;
  1240. procedure TDriveView.RefreshRootNodes(dsFlags: Integer);
  1241. var
  1242. Drives: TStrings;
  1243. NewText: string;
  1244. SaveCursor: TCursor;
  1245. WasValid: Boolean;
  1246. NodeData: TNodeData;
  1247. DriveStatus: TDriveStatus;
  1248. NextDriveNode: TTreeNode;
  1249. Index: Integer;
  1250. Drive: string;
  1251. GetAttr: Boolean;
  1252. begin
  1253. SaveCursor := Screen.Cursor;
  1254. Screen.Cursor := crHourGlass;
  1255. Drives := nil;
  1256. try
  1257. Drives := GetDrives;
  1258. NextDriveNode := nil;
  1259. for Index := Drives.Count - 1 downto 0 do
  1260. begin
  1261. Drive := Drives[Index];
  1262. DriveStatus := GetDriveStatus(Drive);
  1263. if ((dsFlags and dvdsFloppy) <> 0) or DriveInfo.IsFixedDrive(Drive) then
  1264. begin
  1265. with DriveInfo.Get(Drive) do
  1266. begin
  1267. WasValid := Assigned(DriveStatus.RootNode);
  1268. end;
  1269. if ((dsFlags and dvdsReReadAllways) = 0) and
  1270. (Length(DriveInfo.Get(Drive).DisplayName) > 0) then
  1271. dsFlags := dsFlags and (not dsDisplayName);
  1272. DriveInfo.ReadDriveStatus(Drive, dsFlags);
  1273. with DriveInfo.Get(Drive), DriveStatus do
  1274. begin
  1275. if Valid then
  1276. begin
  1277. if not WasValid then
  1278. {New drive has arrived: insert new rootnode:}
  1279. begin
  1280. { Create root directory node }
  1281. NodeData := TNodeData.Create;
  1282. NodeData.DirName := DriveInfo.GetDriveRoot(Drive);
  1283. NodeData.ShortName := NodeData.DirName;
  1284. {Get the shared attributes:}
  1285. GetAttr :=
  1286. DriveInfo.IsFixedDrive(Drive) and (DriveType <> DRIVE_REMOVABLE) and
  1287. ((DriveType <> DRIVE_REMOTE) or GetNetWorkConnected(Drive));
  1288. GetNodeShellAttr(nil, NodeData, GetAttr);
  1289. if Assigned(NextDriveNode) then
  1290. RootNode := Items.InsertObject(NextDriveNode, '', NodeData)
  1291. else
  1292. RootNode := Items.AddObject(nil, '', NodeData);
  1293. RootNode.Text := GetDisplayName(RootNode);
  1294. RootNode.HasChildren := True;
  1295. Scanned := False;
  1296. Verified := False;
  1297. end
  1298. else
  1299. if RootNode.ImageIndex <> DriveInfo.Get(Drive).ImageIndex then
  1300. begin {WasValid = True}
  1301. RootNode.ImageIndex := DriveInfo.Get(Drive).ImageIndex;
  1302. RootNode.SelectedIndex := DriveInfo.Get(Drive).ImageIndex;
  1303. end;
  1304. if Assigned(RootNode) then
  1305. begin
  1306. NewText := GetDisplayName(RootNode);
  1307. if RootNode.Text <> NewText then
  1308. RootNode.Text := NewText;
  1309. end;
  1310. end
  1311. else
  1312. if WasValid then
  1313. {Drive has been removed => delete rootnode:}
  1314. begin
  1315. DriveRemoved(Drive);
  1316. Scanned := False;
  1317. Verified := False;
  1318. RootNode.Delete;
  1319. RootNode := nil;
  1320. end;
  1321. end;
  1322. end;
  1323. if Assigned(DriveStatus.RootNode) then
  1324. NextDriveNode := DriveStatus.RootNode;
  1325. end;
  1326. finally
  1327. Screen.Cursor := SaveCursor;
  1328. Drives.Free;
  1329. end;
  1330. end; {RefreshRootNodes}
  1331. function TDriveView.AddChildNode(ParentNode: TTreeNode; SRec: TSearchRec): TTreeNode;
  1332. var
  1333. NewNode: TTreeNode;
  1334. NodeData: TNodeData;
  1335. GetAttr: Boolean;
  1336. begin
  1337. NodeData := TNodeData.Create;
  1338. NodeData.Attr := SRec.Attr;
  1339. NodeData.DirName := SRec.Name;
  1340. NodeData.ShortName := SRec.FindData.cAlternateFileName;
  1341. NodeData.FIsRecycleBin :=
  1342. (SRec.Attr and faSysFile <> 0) and
  1343. (ParentNode.Level = 0) and
  1344. (SameText(SRec.Name, 'RECYCLED') or
  1345. SameText(SRec.Name, 'RECYCLER') or
  1346. SameText(SRec.Name, '$RECYCLE.BIN'));
  1347. { query content attributes ("has subfolder") only if tree view is visible }
  1348. { to avoid unnecessary scan of subfolders (which may take some time) }
  1349. { if tree view is not visible anyway }
  1350. GetAttr :=
  1351. Visible and
  1352. (GetDriveTypeToNode(ParentNode) <> DRIVE_REMOTE);
  1353. GetNodeShellAttr(ParentNode, NodeData, GetAttr);
  1354. NewNode := Self.Items.AddChildObject(ParentNode, '', NodeData);
  1355. NewNode.Text := GetDisplayName(NewNode);
  1356. Result := NewNode;
  1357. end; {AddChildNode}
  1358. function TDriveView.GetDriveStatus(Drive: string): TDriveStatus;
  1359. begin
  1360. if not FDriveStatus.TryGetValue(Drive, Result) then
  1361. begin
  1362. Result := CreateDriveStatus;
  1363. FDriveStatus.Add(Drive, Result);
  1364. RefreshRootNodes(dsAll or dvdsRereadAllways);
  1365. if Assigned(OnRefreshDrives) then
  1366. OnRefreshDrives(Self);
  1367. end;
  1368. end; {GetDriveStatus}
  1369. function TDriveView.DoScanDir(FromNode: TTreeNode): Boolean;
  1370. begin
  1371. Result := not TNodeData(FromNode.Data).IsRecycleBin;
  1372. end; {DoScanDir}
  1373. function TDriveView.DirAttrMask: Integer;
  1374. begin
  1375. Result := faDirectory or faSysFile;
  1376. if ShowHiddenDirs then
  1377. Result := Result or faHidden;
  1378. end;
  1379. procedure TDriveView.ScanDrive(Drive: string);
  1380. var
  1381. DosError: Integer;
  1382. RootNode: TTreeNode;
  1383. SaveCursor: TCursor;
  1384. procedure ScanPath(const Path: string; ParentNode: TTreeNode);
  1385. var
  1386. SRec: TSearchRec;
  1387. SubNode: TTreeNode;
  1388. begin
  1389. if not DoScanDir(ParentNode) then
  1390. Exit;
  1391. DosError := FindFirst(ApiPath(Path), DirAttrMask, Srec);
  1392. while DosError = 0 do
  1393. begin
  1394. if (SRec.Name <> '.') and
  1395. (SRec.Name <> '..') and
  1396. (SRec.Attr and faDirectory <> 0) then
  1397. begin
  1398. if (SRec.Attr And faDirectory) <> 0 then
  1399. begin { Scan subdirectory }
  1400. SubNode := AddChildNode(ParentNode, SRec);
  1401. TNodeData(SubNode.Data).Scanned := True;
  1402. ScanPath(ExtractFilePath(Path) + SRec.Name + '\*.*', SubNode);
  1403. if not FContinue then
  1404. Break;
  1405. end;
  1406. end;
  1407. DosError := FindNext(SRec);
  1408. end;
  1409. FindClose(Srec);
  1410. if (Items.Count mod 10) = 0 then
  1411. Application.ProcessMessages;
  1412. if not FContinue then
  1413. Exit;
  1414. end; {ScanPath}
  1415. begin {ScanDrive}
  1416. with Self.Items do
  1417. begin
  1418. FContinue := True;
  1419. if not FFullDriveScan then
  1420. begin
  1421. ValidateDirectory(FindNodeToPath(DriveInfo.GetDriveRoot(Drive)));
  1422. GetDriveStatus(Drive).Scanned := True;
  1423. GetDriveStatus(Drive).Verified := False;
  1424. end
  1425. else
  1426. begin
  1427. SaveCursor := Screen.Cursor;
  1428. Screen.Cursor := crHourGlass;
  1429. Items.BeginUpdate;
  1430. try
  1431. RootNode := GetDriveStatus(Drive).RootNode;
  1432. if not Assigned(RootNode) then Exit;
  1433. iF RootNode.HasChildren then
  1434. RootNode.DeleteChildren;
  1435. ScanPath(DriveInfo.GetDriveRoot(Drive) + '*.*', RootNode); { scan subdirectories of rootdir}
  1436. TNodeData(RootNode.Data).Scanned := True;
  1437. GetDriveStatus(Drive).Scanned := True;
  1438. GetDriveStatus(Drive).Verified := True;
  1439. finally
  1440. SortChildren(GetDriveStatus(Drive).RootNode, True);
  1441. EndUpdate;
  1442. end;
  1443. RootNode.Expand(False);
  1444. Screen.Cursor := SaveCursor;
  1445. end;
  1446. end;
  1447. end; {ScanDrive}
  1448. function TDriveView.FindNodeToPath(Path: string): TTreeNode;
  1449. function SearchSubDirs(ParentNode: TTreeNode; Path: string): TTreeNode; forward;
  1450. function DoSearchSubDirs(ParentNode: TTreeNode; Path: string): TTreeNode;
  1451. var
  1452. i: Integer;
  1453. Node: TTreeNode;
  1454. Dir: string;
  1455. begin
  1456. {Extract first directory from path:}
  1457. i := Pos('\', Path);
  1458. if i = 0 then
  1459. i := Length(Path);
  1460. Dir := System.Copy(Path, 1, i);
  1461. System.Delete(Path, 1, i);
  1462. if Dir[Length(Dir)] = '\' then
  1463. SetLength(Dir, Pred(Length(Dir)));
  1464. Node := ParentNode.GetFirstChild;
  1465. if not Assigned(Node) then
  1466. begin
  1467. ValidateDirectoryEx(ParentNode, rsRecursiveExisting, True);
  1468. Node := ParentNode.GetFirstChild;
  1469. end;
  1470. Result := nil;
  1471. while Assigned(Node) do
  1472. begin
  1473. if (UpperCase(GetDirName(Node)) = Dir) or (TNodeData(Node.Data).ShortName = Dir) then
  1474. begin
  1475. if Length(Path) > 0 then
  1476. begin
  1477. Result := SearchSubDirs(Node, Path)
  1478. end
  1479. else
  1480. begin
  1481. Result := Node;
  1482. end;
  1483. Exit;
  1484. end;
  1485. Node := ParentNode.GetNextChild(Node);
  1486. end;
  1487. end;
  1488. function SearchSubDirs(ParentNode: TTreeNode; Path: string): TTreeNode;
  1489. begin
  1490. Result := nil;
  1491. if Length(Path) > 0 then
  1492. begin
  1493. if not TNodeData(ParentNode.Data).Scanned then
  1494. begin
  1495. ReadSubDirs(ParentNode, GetDriveTypetoNode(ParentNode));
  1496. end;
  1497. // Factored out of DoSearchSubDirs is remnant of Bug 956 superceded by Bug 1320
  1498. Result := DoSearchSubDirs(ParentNode, Path);
  1499. end;
  1500. end; {SearchSubDirs}
  1501. var
  1502. Drive: string;
  1503. P: Integer;
  1504. begin {FindNodeToPath}
  1505. Result := nil;
  1506. if Length(Path) < 3 then
  1507. Exit;
  1508. // Particularly when used by TDirView to delegate browsing to
  1509. // hidden drive view, the handle may not be created
  1510. HandleNeeded;
  1511. Drive := DriveInfo.GetDriveKey(Path);
  1512. if Assigned(GetDriveStatus(Drive).RootNode) then
  1513. begin
  1514. if DriveInfo.IsRealDrive(Drive) then
  1515. begin
  1516. System.Delete(Path, 1, 3);
  1517. end
  1518. else
  1519. if IsUncPath(Path) then
  1520. begin
  1521. System.Delete(Path, 1, 2);
  1522. P := Pos('\', Path);
  1523. if P = 0 then
  1524. begin
  1525. Path := '';
  1526. end
  1527. else
  1528. begin
  1529. System.Delete(Path, 1, P);
  1530. P := Pos('\', Path);
  1531. if P = 0 then
  1532. begin
  1533. Path := '';
  1534. end
  1535. else
  1536. begin
  1537. System.Delete(Path, 1, P);
  1538. end;
  1539. end;
  1540. end
  1541. else
  1542. begin
  1543. raise EConvertError.Create(Format(ErrorInvalidDrive, [Path]))
  1544. end;
  1545. if Length(Path) > 0 then
  1546. begin
  1547. if not GetDriveStatus(Drive).Scanned then
  1548. begin
  1549. ScanDrive(Drive);
  1550. end;
  1551. Result := SearchSubDirs(GetDriveStatus(Drive).RootNode, UpperCase(Path));
  1552. end
  1553. else Result := GetDriveStatus(Drive).RootNode;
  1554. end;
  1555. end; {FindNodetoPath}
  1556. function TDriveView.CheckForSubDirs(Path: string): Boolean;
  1557. var
  1558. DosError: Integer;
  1559. SRec: TSearchRec;
  1560. begin
  1561. Result := False;
  1562. DosError := FindFirst(ApiPath(IncludeTrailingBackslash(Path) + '*.'), DirAttrMask, SRec);
  1563. while DosError = 0 do
  1564. begin
  1565. if (SRec.Name <> '.' ) and
  1566. (SRec.Name <> '..') and
  1567. (SRec.Attr and faDirectory <> 0) then
  1568. begin
  1569. Result := True;
  1570. Break;
  1571. end;
  1572. DosError := FindNext(SRec);
  1573. end;
  1574. FindClose(SRec);
  1575. end; {CheckForSubDirs}
  1576. function TDriveView.ReadSubDirs(Node: TTreeNode; DriveType: Integer): Boolean;
  1577. var
  1578. DosError: Integer;
  1579. SRec: TSearchRec;
  1580. NewNode: TTreeNode;
  1581. begin
  1582. Result := False;
  1583. DosError := FindFirst(ApiPath(IncludeTrailingBackslash(NodePath(Node)) + '*.*'), DirAttrMask, SRec);
  1584. while DosError = 0 do
  1585. begin
  1586. if (SRec.Name <> '.' ) and
  1587. (SRec.Name <> '..') and
  1588. (SRec.Attr and faDirectory <> 0) then
  1589. begin
  1590. NewNode := AddChildNode(Node, SRec);
  1591. if DoScanDir(NewNode) then
  1592. begin
  1593. // We have seen the SFGAO_HASSUBFOLDER to be absent on C: drive $Recycle.Bin
  1594. NewNode.HasChildren := Bool(TNodeData(NewNode.Data).shAttr and SFGAO_HASSUBFOLDER);
  1595. TNodeData(NewNode.Data).Scanned := not NewNode.HasChildren;
  1596. end
  1597. else
  1598. begin
  1599. NewNode.HasChildren := False;
  1600. TNodeData(NewNode.Data).Scanned := True;
  1601. end;
  1602. Result := True;
  1603. end;
  1604. DosError := FindNext(SRec);
  1605. end; {While DosError = 0}
  1606. FindClose(Srec);
  1607. TNodeData(Node.Data).Scanned := True;
  1608. if Result then SortChildren(Node, False)
  1609. else Node.HasChildren := False;
  1610. Application.ProcessMessages;
  1611. end; {ReadSubDirs}
  1612. function TDriveView.CallBackValidateDir(var Node: TTreeNode; Data: Pointer): Boolean;
  1613. var
  1614. WorkNode: TTreeNode;
  1615. DelNode: TTreeNode;
  1616. NewNode: TTreeNode;
  1617. SRec: TSearchRec;
  1618. SrecList: TStringList;
  1619. SubDirList: TStringList;
  1620. DosError: Integer;
  1621. Index: Integer;
  1622. NewDirFound: Boolean;
  1623. ParentDir: string;
  1624. NodeData: TNodeData;
  1625. ScanDirInfo: PScanDirInfo;
  1626. begin {CallBackValidateDir}
  1627. Result := True;
  1628. if (not Assigned(Node)) or (not Assigned(Node.Data)) then
  1629. Exit;
  1630. NewDirFound := False;
  1631. ScanDirInfo := PScanDirInfo(Data);
  1632. {Check, if directory still exists: (but not with root directory) }
  1633. if Assigned(Node.Parent) and (ScanDirInfo^.StartNode = Node) then
  1634. if not DirectoryExists(NodePathName(Node)) then
  1635. begin
  1636. WorkNode := Node.Parent;
  1637. if Selected = Node then
  1638. Selected := WorkNode;
  1639. if DropTarget = Node then
  1640. DropTarget := nil;
  1641. Node.Delete;
  1642. Node := nil;
  1643. Exit;
  1644. end;
  1645. WorkNode := Node.GetFirstChild;
  1646. NodeData := TNodeData(Node.Data);
  1647. if NodeData.Scanned and Assigned(WorkNode) then
  1648. {if node was already scanned: check wether the existing subnodes are still alive
  1649. and add all new subdirectories as subnodes:}
  1650. begin
  1651. if DoScanDir(Node) then
  1652. begin
  1653. ParentDir := IncludeTrailingBackslash(NodePath(Node));
  1654. {Build list of existing subnodes:}
  1655. SubDirList := TStringList.Create;
  1656. SubDirList.CaseSensitive := True; // We want to reflect changes in subfolder name case
  1657. while Assigned(WorkNode) do
  1658. begin
  1659. SubDirList.Add(TNodeData(WorkNode.Data).DirName);
  1660. WorkNode := Node.GetNextChild(WorkNode);
  1661. end;
  1662. {Sorting not required, because the subnodes are already sorted!}
  1663. SRecList := TStringList.Create;
  1664. SRecList.CaseSensitive := True;
  1665. DosError := FindFirst(ApiPath(ParentDir + '*.*'), DirAttrMask, SRec);
  1666. while DosError = 0 do
  1667. begin
  1668. if (Srec.Name <> '.' ) and
  1669. (Srec.Name <> '..') and
  1670. (Srec.Attr and faDirectory <> 0) then
  1671. begin
  1672. SrecList.Add(Srec.Name);
  1673. if not SubDirList.Find(Srec.Name, Index) then
  1674. {Subnode does not exists: add it:}
  1675. begin
  1676. NewNode := AddChildNode(Node, SRec);
  1677. NewNode.HasChildren := CheckForSubDirs(ParentDir + Srec.Name);
  1678. TNodeData(NewNode.Data).Scanned := Not NewNode.HasChildren;
  1679. NewDirFound := True;
  1680. end;
  1681. end;
  1682. DosError := FindNext(Srec);
  1683. end;
  1684. FindClose(Srec);
  1685. Sreclist.Sort;
  1686. {Remove not existing subnodes:}
  1687. WorkNode := Node.GetFirstChild;
  1688. while Assigned(WorkNode) do
  1689. begin
  1690. if not Assigned(WorkNode.Data) or
  1691. not SrecList.Find(TNodeData(WorkNode.Data).DirName, Index) then
  1692. begin
  1693. DelNode := WorkNode;
  1694. WorkNode := Node.GetNextChild(WorkNode);
  1695. DelNode.Delete;
  1696. end
  1697. else
  1698. begin
  1699. if (SrecList[Index] <> TNodeData(WorkNode.Data).DirName) then
  1700. begin
  1701. {Case of directory letters has changed:}
  1702. TNodeData(WorkNode.Data).DirName := SrecList[Index];
  1703. TNodeData(WorkNode.Data).ShortName := ExtractShortPathName(NodePathName(WorkNode));
  1704. WorkNode.Text := SrecList[Index];
  1705. end;
  1706. SrecList.Delete(Index);
  1707. WorkNode := Node.GetNextChild(WorkNode);
  1708. end;
  1709. end;
  1710. SrecList.Free;
  1711. SubDirList.Free;
  1712. {Sort subnodes:}
  1713. if NewDirFound then
  1714. SortChildren(Node, False);
  1715. end;
  1716. end
  1717. else
  1718. {Node was not already scanned:}
  1719. if (ScanDirInfo^.SearchNewDirs or
  1720. NodeData.Scanned or
  1721. (Node = ScanDirInfo^.StartNode)) and
  1722. DoScanDir(Node) then
  1723. begin
  1724. ReadSubDirs(Node, ScanDirInfo^.DriveType);
  1725. end;
  1726. end; {CallBackValidateDir}
  1727. procedure TDriveView.RebuildTree;
  1728. var
  1729. Drive: string;
  1730. begin
  1731. for Drive in FDriveStatus.Keys do
  1732. with GetDriveStatus(Drive) do
  1733. if Assigned(RootNode) and Scanned then
  1734. ValidateDirectory(RootNode);
  1735. end;
  1736. procedure TDriveView.ValidateCurrentDirectoryIfNotMonitoring;
  1737. begin
  1738. if Assigned(Selected) and
  1739. not Assigned(GetDriveStatus(GetDriveToNode(Selected)).DiscMonitor) then
  1740. begin
  1741. ValidateDirectory(Selected);
  1742. end;
  1743. end;
  1744. procedure TDriveView.ValidateDirectoryEx(Node: TTreeNode; Recurse: TRecursiveScan;
  1745. NewDirs: Boolean);
  1746. var
  1747. Info: PScanDirInfo;
  1748. SelDir: string;
  1749. SaveCursor: TCursor;
  1750. RestartWatchThread: Boolean;
  1751. SaveCanChange: Boolean;
  1752. CurrentPath: string;
  1753. Drive: string;
  1754. begin
  1755. if Assigned(Node) and Assigned(Node.Data) and
  1756. (not FValidateFlag) and DoScanDir(Node) then
  1757. begin
  1758. SelDir := Directory;
  1759. SaveCursor := Screen.Cursor;
  1760. if Self.Focused and (Screen.Cursor <> crHourGlass) then
  1761. Screen.Cursor := crHourGlass;
  1762. CurrentPath := NodePath(Node);
  1763. Drive := DriveInfo.GetDriveKey(CurrentPath);
  1764. if Node.Level = 0 then
  1765. GetDriveStatus(Drive).ChangeTimer.Enabled := False;
  1766. RestartWatchThread := WatchThreadActive;
  1767. try
  1768. if WatchThreadActive then
  1769. StopWatchThread;
  1770. FValidateFlag := True;
  1771. New(Info);
  1772. Info^.StartNode := Node;
  1773. Info^.SearchNewDirs := NewDirs;
  1774. Info^.DriveType := DriveInfo.Get(Drive).DriveType;
  1775. SaveCanChange := FCanChange;
  1776. FCanChange := True;
  1777. FChangeFlag := False;
  1778. IterateSubTree(Node, CallBackValidateDir, Recurse, coScanStartNode, Info);
  1779. FValidateFlag := False;
  1780. if (not Assigned(Selected)) and (Length(SelDir) > 0) then
  1781. Directory := ExtractFileDrive(SelDir);
  1782. if (SelDir <> Directory) and (not FChangeFlag) then
  1783. Change(Selected);
  1784. FCanChange := SaveCanChange;
  1785. Dispose(Info);
  1786. finally
  1787. if RestartWatchThread and FWatchDirectory and not WatchThreadActive then
  1788. StartWatchThread;
  1789. if Screen.Cursor <> SaveCursor then
  1790. Screen.Cursor := SaveCursor;
  1791. end;
  1792. end;
  1793. end; {ValidateDirectoryEx}
  1794. function TDriveView.GetDriveTypeToNode(Node: TTreeNode): Integer;
  1795. begin
  1796. Assert(Assigned(Node));
  1797. Result := DriveInfo.Get(GetDriveToNode(Node)).DriveType;
  1798. end; {GetDriveTypeToNode}
  1799. procedure TDriveView.CreateWatchThread(Drive: string);
  1800. begin
  1801. if csDesigning in ComponentState then
  1802. Exit;
  1803. if (not Assigned(GetDriveStatus(Drive).DiscMonitor)) and
  1804. FWatchDirectory and
  1805. (DriveInfo.Get(Drive).DriveType <> DRIVE_REMOTE) then
  1806. begin
  1807. with GetDriveStatus(Drive) do
  1808. begin
  1809. DiscMonitor := TDiscMonitor.Create(Self);
  1810. DiscMonitor.ChangeDelay := msThreadChangeDelay;
  1811. DiscMonitor.SubTree := True;
  1812. DiscMonitor.Filters := [moDirName];
  1813. DiscMonitor.OnChange := ChangeDetected;
  1814. DiscMonitor.OnInvalid := ChangeInvalid;
  1815. DiscMonitor.SetDirectory(DriveInfo.GetDriveRoot(Drive));
  1816. DiscMonitor.Open;
  1817. end;
  1818. UpdateDriveNotifications(Drive); // probably noop, as the monitor is not enabled yet
  1819. end;
  1820. end; {CreateWatchThread}
  1821. procedure TDriveView.SetWatchDirectory(Value: Boolean);
  1822. begin
  1823. if FWatchDirectory <> Value then
  1824. begin
  1825. FWatchDirectory := Value;
  1826. if (not (csDesigning in ComponentState)) and Value then
  1827. StartAllWatchThreads
  1828. else
  1829. StopAllWatchThreads;
  1830. end;
  1831. end; {SetAutoScan}
  1832. procedure TDriveView.SetDirView(Value: TDirView);
  1833. begin
  1834. if Assigned(FDirView) then
  1835. FDirView.DriveView := nil;
  1836. FDirView := Value;
  1837. if Assigned(FDirView) then
  1838. FDirView.DriveView := Self;
  1839. end; {SetDirView}
  1840. function TDriveView.NodeWatched(Node: TTreeNode): Boolean;
  1841. var
  1842. Drive: string;
  1843. begin
  1844. Drive := GetDriveToNode(Node);
  1845. Result := WatchThreadActive(Drive);
  1846. end; {NodeWatched}
  1847. procedure TDriveView.ChangeInvalid(Sender: TObject; const Directory: string;
  1848. const ErrorStr: string);
  1849. var
  1850. Drive: string;
  1851. begin
  1852. Drive := DriveInfo.GetDriveKey((Sender as TDiscMonitor).Directories[0]);
  1853. with GetDriveStatus(Drive) do
  1854. begin
  1855. DiscMonitor.Close;
  1856. end;
  1857. UpdateDriveNotifications(Drive);
  1858. end; {DirWatchChangeInvalid}
  1859. procedure TDriveView.ChangeDetected(Sender: TObject; const Directory: string;
  1860. var SubdirsChanged: Boolean);
  1861. var
  1862. DirChanged: string;
  1863. begin
  1864. if Sender is TDiscMonitor then
  1865. begin
  1866. DirChanged := (Sender as TDiscMonitor).Directories[0];
  1867. if Length(DirChanged) > 0 then
  1868. begin
  1869. with GetDriveStatus(DriveInfo.GetDriveKey(DirChanged)) do
  1870. begin
  1871. ChangeTimer.Interval := 0;
  1872. ChangeTimer.Interval := FChangeInterval;
  1873. ChangeTimer.Enabled := True;
  1874. end;
  1875. end;
  1876. end;
  1877. end; {DirWatchChangeDetected}
  1878. procedure TDriveView.ChangeTimerOnTimer(Sender: TObject);
  1879. var
  1880. DriveStatusPair: TDriveStatusPair;
  1881. begin
  1882. if (FChangeTimerSuspended = 0) and (Sender is TTimer) then
  1883. begin
  1884. for DriveStatusPair in FDriveStatus do
  1885. begin
  1886. if DriveStatusPair.Value.ChangeTimer = Sender then
  1887. begin
  1888. // Messages are processed during ValidateDirectory, so we may detect another change while
  1889. // updating the directory. Prevent the recursion.
  1890. // But retry the update afterwards (by reenabling the timer in ChangeDetected)
  1891. SuspendChangeTimer;
  1892. try
  1893. with DriveStatusPair.Value.ChangeTimer do
  1894. begin
  1895. Interval := 0;
  1896. Enabled := False;
  1897. end;
  1898. if Assigned(DriveStatusPair.Value.RootNode) then
  1899. begin
  1900. {Check also collapsed (invisible) subdirectories:}
  1901. ValidateDirectory(DriveStatusPair.Value.RootNode);
  1902. end;
  1903. finally
  1904. ResumeChangeTimer;
  1905. end;
  1906. end;
  1907. end;
  1908. end;
  1909. end; {ChangeTimerOnTimer}
  1910. procedure TDriveView.UpdateDriveNotifications(Drive: string);
  1911. var
  1912. NeedNotifications: Boolean;
  1913. Path: string;
  1914. DevBroadcastHandle: DEV_BROADCAST_HANDLE;
  1915. Size: Integer;
  1916. begin
  1917. if DriveInfo.IsFixedDrive(Drive) then
  1918. begin
  1919. with GetDriveStatus(Drive) do
  1920. begin
  1921. NeedNotifications :=
  1922. WatchThreadActive(Drive) and
  1923. (DriveInfo.Get(Drive).DriveType <> DRIVE_REMOTE) and
  1924. DriveInfo.Get(Drive).DriveReady;
  1925. if NeedNotifications <> (DriveHandle <> INVALID_HANDLE_VALUE) then
  1926. begin
  1927. if NeedNotifications then
  1928. begin
  1929. Path := DriveInfo.GetDriveRoot(Drive);
  1930. DriveHandle :=
  1931. CreateFile(PChar(Path), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil,
  1932. OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL, 0);
  1933. if DriveHandle <> INVALID_HANDLE_VALUE then
  1934. begin
  1935. Size := SizeOf(DevBroadcastHandle);
  1936. ZeroMemory(@DevBroadcastHandle, Size);
  1937. DevBroadcastHandle.dbch_size := Size;
  1938. DevBroadcastHandle.dbch_devicetype := DBT_DEVTYP_HANDLE;
  1939. DevBroadcastHandle.dbch_handle := DriveHandle;
  1940. NotificationHandle :=
  1941. RegisterDeviceNotification(FInternalWindowHandle, @DevBroadcastHandle, DEVICE_NOTIFY_WINDOW_HANDLE);
  1942. if NotificationHandle = nil then
  1943. begin
  1944. CloseHandle(DriveHandle);
  1945. DriveHandle := INVALID_HANDLE_VALUE;
  1946. end;
  1947. end;
  1948. end
  1949. else
  1950. begin
  1951. UnregisterDeviceNotification(NotificationHandle);
  1952. NotificationHandle := nil;
  1953. CloseHandle(DriveHandle);
  1954. DriveHandle := INVALID_HANDLE_VALUE;
  1955. end;
  1956. end;
  1957. end;
  1958. end;
  1959. end;
  1960. procedure TDriveView.StartWatchThread;
  1961. var
  1962. Drive: string;
  1963. begin
  1964. if (csDesigning in ComponentState) or
  1965. not Assigned(Selected) or
  1966. not fWatchDirectory then Exit;
  1967. Drive := GetDriveToNode(Selected);
  1968. with GetDriveStatus(Drive) do
  1969. begin
  1970. if not Assigned(DiscMonitor) then
  1971. CreateWatchThread(Drive);
  1972. if Assigned(DiscMonitor) and not DiscMonitor.Enabled then
  1973. DiscMonitor.Enabled := True;
  1974. end;
  1975. UpdateDriveNotifications(Drive);
  1976. end; {StartWatchThread}
  1977. procedure TDriveView.StopWatchThread;
  1978. var
  1979. Drive: string;
  1980. begin
  1981. if Assigned(Selected) then
  1982. begin
  1983. Drive := GetDriveToNode(Selected);
  1984. with GetDriveStatus(Drive) do
  1985. if Assigned(DiscMonitor) then
  1986. DiscMonitor.Enabled := False;
  1987. UpdateDriveNotifications(Drive);
  1988. end;
  1989. end; {StopWatchThread}
  1990. procedure TDriveView.SuspendChangeTimer;
  1991. begin
  1992. Inc(FChangeTimerSuspended);
  1993. end;
  1994. procedure TDriveView.ResumeChangeTimer;
  1995. begin
  1996. Assert(FChangeTimerSuspended > 0);
  1997. Dec(FChangeTimerSuspended);
  1998. end;
  1999. procedure TDriveView.TerminateWatchThread(Drive: string);
  2000. begin
  2001. with GetDriveStatus(Drive) do
  2002. if Assigned(DiscMonitor) then
  2003. begin
  2004. DiscMonitor.Free;
  2005. DiscMonitor := nil;
  2006. end;
  2007. UpdateDriveNotifications(Drive);
  2008. end; {StopWatchThread}
  2009. procedure TDriveView.StartAllWatchThreads;
  2010. var
  2011. DriveStatusPair: TDriveStatusPair;
  2012. Drive: string;
  2013. begin
  2014. if (csDesigning in ComponentState) or (not FWatchDirectory) then
  2015. Exit;
  2016. for DriveStatusPair in FDriveStatus do
  2017. with DriveStatusPair.Value do
  2018. if Scanned then
  2019. begin
  2020. if not Assigned(DiscMonitor) then
  2021. CreateWatchThread(DriveStatusPair.Key);
  2022. if Assigned(DiscMonitor) and (not DiscMonitor.Active) then
  2023. begin
  2024. DiscMonitor.Open;
  2025. UpdateDriveNotifications(DriveStatusPair.Key);
  2026. end;
  2027. end;
  2028. if Assigned(Selected) then
  2029. begin
  2030. Drive := GetDriveToNode(Selected);
  2031. if not DriveInfo.IsFixedDrive(Drive) then
  2032. begin
  2033. StartWatchThread;
  2034. end;
  2035. end;
  2036. end; {StartAllWatchThreads}
  2037. procedure TDriveView.StopAllWatchThreads;
  2038. var
  2039. DriveStatusPair: TDriveStatusPair;
  2040. begin
  2041. if (csDesigning in ComponentState) or (not FWatchDirectory) then
  2042. Exit;
  2043. for DriveStatusPair in FDriveStatus do
  2044. with DriveStatusPair.Value do
  2045. begin
  2046. if Assigned(DiscMonitor) then
  2047. begin
  2048. DiscMonitor.Close;
  2049. UpdateDriveNotifications(DriveStatusPair.Key);
  2050. end;
  2051. end;
  2052. end; {StopAllWatchThreads}
  2053. function TDriveView.WatchThreadActive(Drive: string): Boolean;
  2054. begin
  2055. Result := FWatchDirectory and
  2056. Assigned(GetDriveStatus(Drive).DiscMonitor) and
  2057. GetDriveStatus(Drive).DiscMonitor.Active and
  2058. GetDriveStatus(Drive).DiscMonitor.Enabled;
  2059. end; {WatchThreadActive}
  2060. function TDriveView.WatchThreadActive: Boolean;
  2061. var
  2062. Drive: string;
  2063. begin
  2064. if not Assigned(Selected) then
  2065. begin
  2066. Result := False;
  2067. Exit;
  2068. end;
  2069. Drive := GetDriveToNode(Selected);
  2070. Result := WatchThreadActive(Drive);
  2071. end; {WatchThreadActive}
  2072. procedure TDriveView.SetFullDriveScan(DoFullDriveScan: Boolean);
  2073. begin
  2074. FFullDriveScan := DoFullDriveScan;
  2075. end; {SetAutoScan}
  2076. function TDriveView.FindPathNode(Path: string): TTreeNode;
  2077. var
  2078. PossiblyHiddenPath: string;
  2079. Attrs: Integer;
  2080. begin
  2081. if Assigned(FOnNeedHiddenDirectories) and
  2082. (not ShowHiddenDirs) and
  2083. DirectoryExistsFix(Path) then // do not even bother if the path does not exist
  2084. begin
  2085. PossiblyHiddenPath := ExcludeTrailingPathDelimiter(Path);
  2086. while (PossiblyHiddenPath <> '') and
  2087. (not IsRootPath(PossiblyHiddenPath)) do // Drives have hidden attribute
  2088. begin
  2089. Attrs := FileGetAttr(PossiblyHiddenPath, False);
  2090. if (Attrs and faHidden) = faHidden then
  2091. begin
  2092. if Assigned(FOnNeedHiddenDirectories) then
  2093. begin
  2094. FOnNeedHiddenDirectories(Self);
  2095. end;
  2096. Break;
  2097. end
  2098. else
  2099. begin
  2100. PossiblyHiddenPath := ExtractFileDir(PossiblyHiddenPath);
  2101. end;
  2102. end;
  2103. end;
  2104. {Find existing path or parent path of not existing path:}
  2105. repeat
  2106. Result := FindNodeToPath(Path);
  2107. if not Assigned(Result) then
  2108. Path := ExtractFilePath(ExcludeTrailingBackslash(Path));
  2109. until Assigned(Result) or (Length(Path) < 3);
  2110. end;
  2111. procedure TDriveView.SetDirectory(Value: string);
  2112. begin
  2113. Value := IncludeTrailingBackslash(Value);
  2114. FDirectory := Value;
  2115. inherited;
  2116. if Assigned(Selected) and (Selected.Level = 0) then
  2117. begin
  2118. if not GetDriveStatus(GetDriveToNode(Selected)).Scanned then
  2119. ScanDrive(GetDriveToNode(Selected));
  2120. end;
  2121. end; {SetDirectory}
  2122. function TDriveView.GetDirName(Node: TTreeNode): string;
  2123. begin
  2124. if Assigned(Node) and Assigned(Node.Data) then
  2125. Result := TNodeData(Node.Data).DirName
  2126. else
  2127. Result := '';
  2128. end; {GetDirName}
  2129. {GetDrive: returns the drive of the Node.}
  2130. function TDriveView.GetDriveToNode(Node: TTreeNode): string;
  2131. var
  2132. Path: string;
  2133. begin
  2134. if (not Assigned (Node)) or (not Assigned(Node.Data)) then
  2135. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['GetDrive']));
  2136. Path := NodePath(Node);
  2137. Result := DriveInfo.GetDriveKey(Path);
  2138. end; {GetDrive}
  2139. {RootNode: returns the rootnode to the Node:}
  2140. function TDriveView.RootNode(Node: TTreeNode): TTreeNode;
  2141. begin
  2142. Result := Node;
  2143. if not Assigned(Node) then
  2144. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['RootNode']));
  2145. while Assigned(Result.Parent) do
  2146. Result := Result.Parent;
  2147. end; {RootNode}
  2148. function TDriveView.GetDisplayName(Node: TTreeNode): string;
  2149. begin
  2150. Result := '';
  2151. if (not Assigned(Node)) or (not Assigned(Node.Data)) then
  2152. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['GetDisplayName']));
  2153. if Node.Level = 0 then Result := GetDriveText(GetDriveToNode(Node))
  2154. else
  2155. begin
  2156. Result := GetDirName(Node);
  2157. end;
  2158. end; {GetDisplayName}
  2159. procedure TDriveView.SetShowVolLabel(ShowIt: Boolean);
  2160. begin
  2161. if ShowIt = FShowVolLabel then
  2162. Exit;
  2163. FShowVolLabel := ShowIt;
  2164. RefreshRootNodes(dvdsFloppy);
  2165. end; {SetShowVolLabel}
  2166. procedure TDriveView.DisplayContextMenu(Node: TTreeNode; Point: TPoint);
  2167. var
  2168. Verb: string;
  2169. DirWatched: Boolean;
  2170. begin
  2171. DirWatched := NodeWatched(Node) and WatchThreadActive;
  2172. Assert(Node <> nil);
  2173. if Node <> Selected then
  2174. DropTarget := Node;
  2175. Verb := EmptyStr;
  2176. if Assigned(FOnDisplayContextMenu) then
  2177. FOnDisplayContextMenu(Self);
  2178. ShellDisplayContextMenu(FParentForm.Handle, Point, NodePathName(Node),
  2179. CanEdit(Node), Verb, False);
  2180. if Verb = shcRename then Node.EditText
  2181. else
  2182. if Verb = shcCut then
  2183. begin
  2184. LastClipBoardOperation := cboCut;
  2185. LastPathCut := NodePathName(Node);
  2186. end
  2187. else
  2188. if Verb = shcCopy then LastClipBoardOperation := cboCopy
  2189. else
  2190. if Verb = shcPaste then
  2191. PasteFromClipBoard(NodePathName(Node));
  2192. DropTarget := nil;
  2193. if not DirWatched then
  2194. ValidateDirectory(Node);
  2195. end; {DisplayContextMenu (2)}
  2196. procedure TDriveView.DisplayPropertiesMenu(Node: TTreeNode);
  2197. begin
  2198. Assert(Assigned(Node));
  2199. ShellExecuteContextCommand(FParentForm.Handle, shcProperties, NodePathName(Node));
  2200. end; {ContextMenu}
  2201. procedure TDriveView.SetSelected(Node: TTreeNode);
  2202. begin
  2203. if Node <> Selected then
  2204. begin
  2205. FChangeFlag := False;
  2206. FCanChange := True;
  2207. inherited Selected := Node;
  2208. if not FChangeFlag then
  2209. Change(Selected);
  2210. end;
  2211. end; {SetSelected}
  2212. {Called by TFileDeleteThread, when a file deletion was detected by the D&D receiving application:}
  2213. procedure TDriveView.SignalDirDelete(Sender: TObject; Files: TStringList);
  2214. begin
  2215. if Files.Count > 0 then
  2216. ValidateDirectory(FindNodeToPath(Files[0]));
  2217. end; {SignalDirDelete}
  2218. function TDriveView.DDSourceEffects: TDropEffectSet;
  2219. begin
  2220. if FDragNode.Level = 0 then
  2221. Result := [deLink]
  2222. else
  2223. Result := [deLink, deCopy, deMove];
  2224. end;
  2225. procedure TDriveView.DDChooseEffect(KeyState: Integer; var Effect: Integer; PreferredEffect: Integer);
  2226. begin
  2227. if DropTarget = nil then Effect := DROPEFFECT_NONE
  2228. else
  2229. if (KeyState and (MK_CONTROL or MK_SHIFT) = 0) and (PreferredEffect = 0) then
  2230. begin
  2231. if FDragDrive <> '' then
  2232. begin
  2233. if FExeDrag and DriveInfo.IsFixedDrive(GetDriveToNode(DropTarget)) and DriveInfo.IsFixedDrive(FDragDrive) then
  2234. begin
  2235. Effect := DROPEFFECT_LINK;
  2236. end
  2237. else
  2238. if (Effect = DROPEFFECT_COPY) and
  2239. (SameText(FDragDrive, GetDriveToNode(DropTarget)) and
  2240. (FDragDropFilesEx.AvailableDropEffects and DROPEFFECT_MOVE <> 0)) then
  2241. begin
  2242. Effect := DROPEFFECT_MOVE;
  2243. end;
  2244. end;
  2245. end;
  2246. inherited;
  2247. end;
  2248. function TDriveView.DragCompleteFileList: Boolean;
  2249. begin
  2250. Result := (GetDriveTypeToNode(FDragNode) <> DRIVE_REMOVABLE);
  2251. end;
  2252. function TDriveView.DDExecute: TDragResult;
  2253. var
  2254. WatchThreadOK: Boolean;
  2255. DragParentPath: string;
  2256. DragPath: string;
  2257. begin
  2258. WatchThreadOK := WatchThreadActive;
  2259. Result := FDragDropFilesEx.Execute(nil);
  2260. if (Result = drMove) and (not WatchThreadOK) then
  2261. begin
  2262. DragPath := NodePathName(FDragNode);
  2263. if Assigned(FDragNode.Parent) then
  2264. DragParentPath := NodePathName(FDragNode.Parent)
  2265. else
  2266. DragParentPath := DragPath;
  2267. if (FDragNode.Level > 0) or (DragParentPath <> NodePathName(Selected.Parent)) then
  2268. begin
  2269. FDragNode := FindNodeToPath(DragPath);
  2270. if Assigned(FDragNode) then
  2271. begin
  2272. FDragFileList.Clear;
  2273. FDragFileList.Add(DragPath);
  2274. TFileDeleteThread.Create(FDragFileList, MaxWaitTimeOut, SignalDirDelete);
  2275. end;
  2276. end;
  2277. end;
  2278. end;
  2279. procedure TDriveView.PerformDragDropFileOperation(Node: TTreeNode; Effect: Integer);
  2280. var
  2281. Index: Integer;
  2282. SourcePath: string;
  2283. SourceParentPath: string;
  2284. SourceIsDirectory: Boolean;
  2285. SaveCursor: TCursor;
  2286. TargetNode: TTreeNode;
  2287. TargetPath: string;
  2288. IsRecycleBin: Boolean;
  2289. begin
  2290. TargetPath := NodePathName(Node);
  2291. IsRecycleBin := NodeIsRecycleBin(Node);
  2292. if FDragDropFilesEx.FileList.Count = 0 then
  2293. Exit;
  2294. SaveCursor := Screen.Cursor;
  2295. Screen.Cursor := crHourGlass;
  2296. SourcePath := EmptyStr;
  2297. try
  2298. if (Effect = DROPEFFECT_COPY) or (Effect = DROPEFFECT_MOVE) then
  2299. begin
  2300. StopAllWatchThreads;
  2301. if Assigned(FDirView) then
  2302. FDirView.StopWatchThread;
  2303. if Assigned(DropSourceControl) and
  2304. (DropSourceControl is TDirView) and
  2305. (DropSourceControl <> FDirView) then
  2306. begin
  2307. TDirView(DropSourceControl).StopWatchThread;
  2308. end;
  2309. if DropFiles(
  2310. DragDropFilesEx, Effect, FFileOperator, TargetPath, false, IsRecycleBin, ConfirmDelete, ConfirmOverwrite, False,
  2311. Self, OnDDFileOperation, SourcePath, SourceIsDirectory) then
  2312. begin
  2313. if Assigned(FOnDDFileOperationExecuted) then
  2314. FOnDDFileOperationExecuted(Self, Effect, SourcePath, TargetPath);
  2315. end;
  2316. ClearDragFileList(FDragDropFilesEx.FileList);
  2317. SourceParentPath := ExtractFilePath(ExcludeTrailingBackslash(SourcePath));
  2318. end
  2319. else
  2320. if Effect = DROPEFFECT_LINK then
  2321. { Create Link requested: }
  2322. begin
  2323. for Index := 0 to FDragDropFilesEx.FileList.Count - 1 do
  2324. begin
  2325. if not DropLink(PFDDListItem(FDragDropFilesEx.FileList[Index]), TargetPath) then
  2326. begin
  2327. DDError(DDCreateShortCutError);
  2328. end;
  2329. end;
  2330. end;
  2331. if Effect = DROPEFFECT_MOVE then
  2332. Items.BeginUpdate;
  2333. {Update source directory, if move-operation was performed:}
  2334. if ((Effect = DROPEFFECT_MOVE) or IsRecycleBin) then
  2335. begin
  2336. ValidateDirectory(FindNodeToPath(SourceParentPath));
  2337. end;
  2338. {Update subdirectories of target directory:}
  2339. TargetNode := FindNodeToPath(TargetPath);
  2340. if Assigned(TargetNode) then
  2341. ValidateDirectory(TargetNode)
  2342. else
  2343. ValidateDirectory(GetDriveStatus(DriveInfo.GetDriveKey(TargetPath)).RootNode);
  2344. if Effect = DROPEFFECT_MOVE then
  2345. Items.EndUpdate;
  2346. {Update linked component TDirView:}
  2347. if Assigned(FDirView) and (not FDirView.WatchThreadActive) then
  2348. begin
  2349. case Effect of
  2350. DROPEFFECT_COPY,
  2351. DROPEFFECT_LINK:
  2352. if (IncludeTrailingBackslash(TargetPath) = IncludeTrailingBackslash(DirView.Path)) then
  2353. FDirView.Reload2;
  2354. DROPEFFECT_MOVE:
  2355. if (IncludeTrailingBackslash(TargetPath) = IncludeTrailingBackslash(DirView.Path)) or
  2356. (IncludeTrailingBackslash(SourceParentPath) = IncludeTrailingBackslash(DirView.Path)) then
  2357. begin
  2358. if FDirView <> DropSourceControl then FDirView.Reload2;
  2359. end;
  2360. end; {Case}
  2361. end;
  2362. {Update the DropSource control, if files are moved and it is a TDirView:}
  2363. if (Effect = DROPEFFECT_MOVE) and (DropSourceControl is TDirView) then
  2364. begin
  2365. TDirView(DropSourceControl).ValidateSelectedFiles;
  2366. end;
  2367. finally
  2368. FFileOperator.OperandFrom.Clear;
  2369. FFileOperator.OperandTo.Clear;
  2370. StartAllWatchThreads;
  2371. if Assigned(FDirView) and (not FDirView.WatchThreadActive) then
  2372. FDirView.StartWatchThread;
  2373. if Assigned(DropSourceControl) and (DropSourceControl is TDirView) and
  2374. (not TDirView(DropSourceControl).WatchThreadActive) then
  2375. TDirView(DropSourceControl).StartWatchThread;
  2376. Screen.Cursor := SaveCursor;
  2377. end;
  2378. end; {PerformDragDropFileOperation}
  2379. function TDriveView.GetCanUndoCopyMove: Boolean;
  2380. begin
  2381. Result := Assigned(FFileOperator) and FFileOperator.CanUndo;
  2382. end; {CanUndoCopyMove}
  2383. function TDriveView.UndoCopyMove: Boolean;
  2384. var
  2385. LastTarget: string;
  2386. LastSource: string;
  2387. begin
  2388. Result := False;
  2389. if FFileOperator.CanUndo then
  2390. begin
  2391. Lasttarget := FFileOperator.LastOperandTo[0];
  2392. LastSource := FFileOperator.LastOperandFrom[0];
  2393. StopAllWatchThreads;
  2394. Result := FFileOperator.UndoExecute;
  2395. ValidateDirectory(FindNodeToPath(ExtractFilePath(LastTarget)));
  2396. ValidateDirectory(FindNodeToPath(ExtractFilePath(LastSource)));
  2397. StartAllWatchThreads;
  2398. if Assigned(FDirView) then
  2399. with FDirView do
  2400. if not WatchThreadActive then
  2401. begin
  2402. if (IncludeTrailingBackslash(ExtractFilePath(LastTarget)) = IncludeTrailingBackslash(Path)) or
  2403. (IncludeTrailingBackslash(ExtractFilePath(LastSource)) = IncludeTrailingBackslash(Path)) then
  2404. Reload2;
  2405. end;
  2406. end;
  2407. end; {UndoCopyMove}
  2408. {Clipboard operations:}
  2409. procedure TDriveView.SetLastPathCut(Path: string);
  2410. var
  2411. Node: TTreeNode;
  2412. begin
  2413. if FLastPathCut <> Path then
  2414. begin
  2415. Node := FindNodeToPath(FLastPathCut);
  2416. if Assigned(Node) then
  2417. begin
  2418. FLastPathCut := Path;
  2419. Node.Cut := False;
  2420. end;
  2421. Node := FindNodeToPath(Path);
  2422. if Assigned(Node) then
  2423. begin
  2424. FLastPathCut := Path;
  2425. Node.Cut := True;
  2426. end;
  2427. end;
  2428. end; {SetLastNodeCut}
  2429. procedure TDriveView.EmptyClipboard;
  2430. begin
  2431. if Windows.OpenClipBoard(0) then
  2432. begin
  2433. Windows.EmptyClipBoard;
  2434. Windows.CloseClipBoard;
  2435. LastPathCut := '';
  2436. LastClipBoardOperation := cboNone;
  2437. if Assigned(FDirView) then
  2438. FDirView.EmptyClipboard;
  2439. end;
  2440. end; {EmptyClipBoard}
  2441. function TDriveView.CopyToClipBoard(Node: TTreeNode): Boolean;
  2442. begin
  2443. Result := Assigned(Selected);
  2444. if Result then
  2445. begin
  2446. EmptyClipBoard;
  2447. ClearDragFileList(FDragDropFilesEx.FileList);
  2448. AddToDragFileList(FDragDropFilesEx.FileList, Selected);
  2449. Result := FDragDropFilesEx.CopyToClipBoard;
  2450. LastClipBoardOperation := cboCopy;
  2451. end;
  2452. end; {CopyToClipBoard}
  2453. function TDriveView.CutToClipBoard(Node: TTreeNode): Boolean;
  2454. begin
  2455. Result := Assigned(Node) and (Node.Level > 0) and CopyToClipBoard(Node);
  2456. if Result then
  2457. begin
  2458. LastPathCut := NodePathName(Node);
  2459. LastClipBoardOperation := cboCut;
  2460. end;
  2461. end; {CutToClipBoard}
  2462. function TDriveView.CanPasteFromClipBoard: Boolean;
  2463. begin
  2464. Result := False;
  2465. if Assigned(Selected) and Windows.OpenClipboard(0) then
  2466. begin
  2467. Result := IsClipboardFormatAvailable(CF_HDROP);
  2468. Windows.CloseClipBoard;
  2469. end;
  2470. end; {CanPasteFromClipBoard}
  2471. function TDriveView.PasteFromClipBoard(TargetPath: String = ''): Boolean;
  2472. begin
  2473. ClearDragFileList(FDragDropFilesEx.FileList);
  2474. Result := False;
  2475. if CanPasteFromClipBoard and {MP}FDragDropFilesEx.GetFromClipBoard{/MP}
  2476. then
  2477. begin
  2478. if TargetPath = '' then
  2479. TargetPath := NodePathName(Selected);
  2480. case LastClipBoardOperation of
  2481. cboCopy,
  2482. cboNone:
  2483. begin
  2484. PerformDragDropFileOperation(Selected, DROPEFFECT_COPY);
  2485. if Assigned(FOnDDExecuted) then
  2486. FOnDDExecuted(Self, DROPEFFECT_COPY);
  2487. end;
  2488. cboCut:
  2489. begin
  2490. PerformDragDropFileOperation(Selected, DROPEFFECT_MOVE);
  2491. if Assigned(FOnDDExecuted) then
  2492. FOnDDExecuted(Self, DROPEFFECT_MOVE);
  2493. EmptyClipBoard;
  2494. end;
  2495. end;
  2496. Result := True;
  2497. end;
  2498. end; {PasteFromClipBoard}
  2499. end.