DriveView.pas 81 KB

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