DriveView.pas 82 KB

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