DriveView.pas 83 KB

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