DriveView.pas 82 KB

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